| /* |
| * 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.jclouds.osgi; |
| |
| import static org.jclouds.osgi.Bundles.instantiateAvailableClasses; |
| import static org.jclouds.osgi.Bundles.stringsForResourceInBundle; |
| import static org.osgi.framework.BundleEvent.STARTED; |
| import static org.osgi.framework.BundleEvent.STOPPED; |
| import static org.osgi.framework.BundleEvent.STOPPING; |
| |
| import java.util.List; |
| |
| import org.jclouds.apis.ApiMetadata; |
| import org.jclouds.providers.ProviderMetadata; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleListener; |
| |
| import com.google.common.collect.ArrayListMultimap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Multimap; |
| |
| /** |
| * A {@link BundleListener} that listens for {@link BundleEvent} and searches for |
| * {@link org.jclouds.providers.ProviderMetadata} and {@link org.jclouds.apis.ApiMetadata} in newly installed Bundles. |
| * This is used as a workaround for OSGi environments where the ServiceLoader cannot cross bundle boundaries. |
| */ |
| public class MetadataBundleListener implements BundleListener { |
| |
| private final Multimap<Long, ProviderMetadata> providerMetadataMap = ArrayListMultimap.create(); |
| private final Multimap<Long, ApiMetadata> apiMetadataMap = ArrayListMultimap.create(); |
| |
| private final List<ProviderListener> providerListeners = Lists.newArrayList(); |
| private final List<ApiListener> apiListeners = Lists.newArrayList(); |
| |
| /** |
| * Starts the listener. Checks the bundles that are already active and registers {@link ProviderMetadata} and |
| * {@link ApiMetadata} found. Registers the itself as a {@link BundleListener}. |
| * |
| * @param bundleContext |
| */ |
| public synchronized void start(BundleContext bundleContext) { |
| bundleContext.addBundleListener(this); |
| for (Bundle bundle : bundleContext.getBundles()) { |
| if (bundle.getState() == Bundle.ACTIVE) { |
| addBundle(bundle); |
| } |
| } |
| bundleContext.addBundleListener(this); |
| } |
| |
| /** |
| * Stops the listener. Removes itself from the {@link BundleListener}s. Clears metadata maps and listeners lists. |
| * |
| * @param bundleContext |
| */ |
| public void stop(BundleContext bundleContext) { |
| bundleContext.removeBundleListener(this); |
| providerMetadataMap.clear(); |
| apiMetadataMap.clear(); |
| apiListeners.clear(); |
| providerListeners.clear(); |
| } |
| |
| @Override |
| public synchronized void bundleChanged(BundleEvent event) { |
| switch (event.getType()) { |
| case STARTED: |
| addBundle(event.getBundle()); |
| break; |
| case STOPPING: |
| case STOPPED: |
| removeBundle(event.getBundle()); |
| break; |
| } |
| } |
| |
| /** |
| * Searches for {@link ProviderMetadata} and {@link ApiMetadata} inside the {@link Bundle}. If metadata are found |
| * they are registered in the {@link ProviderRegistry} and {@link ApiRegistry}. Also the {@link ProviderListener} and |
| * {@link ApiListener} are notified. |
| * |
| * @param bundle |
| */ |
| private synchronized void addBundle(Bundle bundle) { |
| for (ProviderMetadata providerMetadata : listProviderMetadata(bundle)) { |
| if (providerMetadata != null) { |
| ProviderRegistry.registerProvider(providerMetadata); |
| providerMetadataMap.put(bundle.getBundleId(), providerMetadata); |
| for (ProviderListener listener : providerListeners) { |
| listener.added(providerMetadata); |
| } |
| } |
| } |
| |
| for (ApiMetadata apiMetadata : listApiMetadata(bundle)) { |
| if (apiMetadata != null) { |
| ApiRegistry.registerApi(apiMetadata); |
| apiMetadataMap.put(bundle.getBundleId(), apiMetadata); |
| for (ApiListener listener : apiListeners) { |
| listener.added(apiMetadata); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Searches for {@link ProviderMetadata} and {@link ApiMetadata} registered under the {@link Bundle} id. If metadata |
| * are found they are removed the {@link ProviderRegistry} and {@link ApiRegistry}. Also the {@link ProviderListener} |
| * and {@link ApiListener} are notified. |
| * |
| * @param bundle |
| */ |
| private synchronized void removeBundle(Bundle bundle) { |
| for (ProviderMetadata providerMetadata : providerMetadataMap.removeAll(bundle.getBundleId())) { |
| ProviderRegistry.unregisterProvider(providerMetadata); |
| for (ProviderListener listener : providerListeners) { |
| listener.removed(providerMetadata); |
| } |
| } |
| for (ApiMetadata apiMetadata : apiMetadataMap.removeAll(bundle.getBundleId())) { |
| ApiRegistry.unRegisterApi(apiMetadata); |
| for (ApiListener listener : apiListeners) { |
| listener.removed(apiMetadata); |
| } |
| } |
| } |
| |
| /** |
| * Creates an instance of {@link ProviderMetadata} from the {@link Bundle}. |
| * |
| * @param bundle |
| * @return |
| */ |
| public Iterable<ProviderMetadata> listProviderMetadata(Bundle bundle) { |
| Iterable<String> classNames = stringsForResourceInBundle( |
| "/META-INF/services/org.jclouds.providers.ProviderMetadata", bundle); |
| return instantiateAvailableClasses(bundle, classNames, ProviderMetadata.class); |
| } |
| |
| /** |
| * Creates an instance of {@link ApiMetadata} from the {@link Bundle}. |
| * |
| * @param bundle |
| * @return |
| */ |
| public Iterable<ApiMetadata> listApiMetadata(Bundle bundle) { |
| Iterable<String> classNames = stringsForResourceInBundle("/META-INF/services/org.jclouds.apis.ApiMetadata", bundle); |
| return instantiateAvailableClasses(bundle, classNames, ApiMetadata.class); |
| } |
| |
| /** |
| * Adds a {@link ProviderListener} and notifies it of existing {@link ProviderMetadata}. |
| * |
| * @param listener |
| * The listener. |
| */ |
| public synchronized void addProviderListener(ProviderListener listener) { |
| providerListeners.add(listener); |
| for (ProviderMetadata metadata : providerMetadataMap.values()) { |
| listener.added(metadata); |
| } |
| } |
| |
| /** |
| * Removes the {@link ProviderListener} |
| * |
| * @param listener |
| * The listener |
| */ |
| public synchronized void removeProviderListener(ProviderListener listener) { |
| providerListeners.remove(listener); |
| } |
| |
| /** |
| * Adds a {@link ApiListener} and notifies it of existing {@link ApiMetadata}. |
| * |
| * @param listener |
| */ |
| public synchronized void addApiListenerListener(ApiListener listener) { |
| apiListeners.add(listener); |
| for (ApiMetadata metadata : apiMetadataMap.values()) { |
| listener.added(metadata); |
| } |
| } |
| |
| /** |
| * Removes the {@link ApiListener} |
| * |
| * @param listener |
| */ |
| public synchronized void removeApiListenerListener(ApiListener listener) { |
| apiListeners.remove(listener); |
| } |
| |
| } |