/* * 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.blueprint.container; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.aries.blueprint.di.Recipe; import org.apache.aries.quiesce.manager.QuiesceCallback; import org.apache.aries.quiesce.participant.QuiesceParticipant; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; public class BlueprintQuiesceParticipant implements QuiesceParticipant { private final BundleContext ctx; private final BlueprintExtender extender; public BlueprintQuiesceParticipant(BundleContext context, BlueprintExtender extender) { this.ctx = context; this.extender = extender; } /** * A Threadpool for running quiesce operations */ private final ExecutorService executor = new ThreadPoolExecutor(0, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r, "Blueprint-Container-ThreadPool"); t.setDaemon(true); return t; } }); public void quiesce(QuiesceCallback callback, List<Bundle> bundlesToQuiesce) { boolean shutdownMe = false; for(Bundle b : bundlesToQuiesce) { try { executor.execute(new QuiesceBundle(callback, b, extender)); } catch (RejectedExecutionException re) { } //If we are quiescing, then we need to quiesce this threadpool! shutdownMe |= b.equals(ctx.getBundle()); } if (shutdownMe) executor.shutdown(); } /** * A runnable Quiesce operation for a single bundle */ private static final class QuiesceBundle implements Runnable { /** The bundle being quiesced */ private final Bundle bundleToQuiesce; private final QuiesceCallback callback; private final BlueprintExtender extender; public QuiesceBundle(QuiesceCallback callback, Bundle bundleToQuiesce, BlueprintExtender extender) { super(); this.callback = callback; this.bundleToQuiesce = bundleToQuiesce; this.extender = extender; } public void run() { BlueprintContainerImpl container = extender.getBlueprintContainerImpl(bundleToQuiesce); // have we got an actual blueprint bundle if (container != null) { BlueprintRepository repository = container.getRepository(); Set<String> names = repository.getNames(); container.quiesce(); QuiesceDelegatingCallback qdcbk = new QuiesceDelegatingCallback(callback, bundleToQuiesce); for (String name: names) { Recipe recipe = repository.getRecipe(name); if (recipe instanceof ServiceRecipe) { qdcbk.callCountDown.incrementAndGet(); ((ServiceRecipe)recipe).quiesce(qdcbk); } } //Either there were no services and we win, or there were services but they //have all finished and we win, or they still have tidy up to do, but we //end up at 0 eventually qdcbk.callback(); } else { // for non-Blueprint bundles just call return completed callback.bundleQuiesced(bundleToQuiesce); } } } /** * A wrapper to protect our internals from the Quiesce API so that we can make it * an optional dependency */ private static final class QuiesceDelegatingCallback implements DestroyCallback { /** The callback to delegate to */ private final QuiesceCallback callback; /** The single bundle being quiesced by this DestroyCallback */ private final Bundle toQuiesce; /** A countdown that starts at one so it can't finish before we do! */ private final AtomicInteger callCountDown = new AtomicInteger(1); public QuiesceDelegatingCallback(QuiesceCallback cbk, Bundle b) { callback = cbk; toQuiesce = b; } public void callback() { if (callCountDown.decrementAndGet() == 0) { callback.bundleQuiesced(toQuiesce); } } } }