| /** |
| * 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.compendium.cm; |
| |
| import java.util.Dictionary; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.apache.aries.blueprint.utils.JavaUtils; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.cm.ConfigurationException; |
| import org.osgi.service.cm.ManagedServiceFactory; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public abstract class BaseManagedServiceFactory<T> implements ManagedServiceFactory { |
| |
| public static final long DEFAULT_TIMEOUT_BEFORE_INTERRUPT = 30000; |
| |
| public static final int CONFIGURATION_ADMIN_OBJECT_DELETED = 1; |
| |
| public static final int BUNDLE_STOPPING = 2; |
| |
| public static final int INTERNAL_ERROR = 4; |
| |
| protected final Logger LOGGER = LoggerFactory.getLogger(getClass()); |
| |
| private final BundleContext context; |
| private final String name; |
| private final long timeoutBeforeInterrupt; |
| private final AtomicBoolean destroyed; |
| private final ExecutorService executor; |
| private final Map<String, Pair<T, ServiceRegistration>> services; |
| private final Map<ServiceRegistration, T> registrations; |
| |
| public BaseManagedServiceFactory(BundleContext context, String name) { |
| this(context, name, DEFAULT_TIMEOUT_BEFORE_INTERRUPT); |
| } |
| |
| public BaseManagedServiceFactory(BundleContext context, String name, long timeoutBeforeInterrupt) { |
| this.context = context; |
| this.name = name; |
| this.timeoutBeforeInterrupt = timeoutBeforeInterrupt; |
| this.destroyed = new AtomicBoolean(false); |
| this.executor = Executors.newSingleThreadExecutor(); |
| this.services = new ConcurrentHashMap<String, Pair<T, ServiceRegistration>>(); |
| this.registrations = new ConcurrentHashMap<ServiceRegistration, T>(); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public Map<ServiceRegistration, T> getServices() { |
| return registrations; |
| } |
| |
| public void updated(final String pid, final Dictionary properties) throws ConfigurationException { |
| if (destroyed.get()) { |
| return; |
| } |
| checkConfiguration(pid, properties); |
| executor.submit(new Runnable() { |
| public void run() { |
| try { |
| internalUpdate(pid, properties); |
| } catch (Throwable t) { |
| LOGGER.warn("Error destroying service for ManagedServiceFactory " + getName(), t); |
| } |
| } |
| }); |
| } |
| |
| public void deleted(final String pid) { |
| if (destroyed.get()) { |
| return; |
| } |
| executor.submit(new Runnable() { |
| public void run() { |
| try { |
| internalDelete(pid, CONFIGURATION_ADMIN_OBJECT_DELETED); |
| } catch (Throwable throwable) { |
| LOGGER.warn("Error destroying service for ManagedServiceFactory " + getName(), throwable); |
| } |
| } |
| }); |
| } |
| |
| protected void checkConfiguration(String pid, Dictionary properties) throws ConfigurationException { |
| // Do nothing |
| } |
| |
| protected abstract T doCreate(Dictionary properties) throws Exception; |
| |
| protected abstract T doUpdate(T t, Dictionary properties) throws Exception; |
| |
| protected abstract void doDestroy(T t, Dictionary properties, int code) throws Exception; |
| |
| protected abstract String[] getExposedClasses(T t); |
| |
| private void internalUpdate(String pid, Dictionary properties) { |
| Pair<T, ServiceRegistration> pair = services.get(pid); |
| if (pair != null) { |
| try { |
| T t = doUpdate(pair.getFirst(), properties); |
| pair.setFirst(t); |
| pair.getSecond().setProperties(properties); |
| } catch (Throwable throwable) { |
| internalDelete(pid, INTERNAL_ERROR); |
| LOGGER.warn("Error updating service for ManagedServiceFactory " + getName(), throwable); |
| } |
| } else { |
| if (destroyed.get()) { |
| return; |
| } |
| try { |
| T t = doCreate(properties); |
| try { |
| if (destroyed.get()) { |
| throw new IllegalStateException("ManagedServiceFactory has been destroyed"); |
| } |
| ServiceRegistration registration = context.registerService(getExposedClasses(t), t, properties); |
| services.put(pid, new Pair<T, ServiceRegistration>(t, registration)); |
| registrations.put(registration, t); |
| postRegister(t, properties, registration); |
| } catch (Throwable throwable1) { |
| try { |
| doDestroy(t, properties, INTERNAL_ERROR); |
| } catch (Throwable throwable2) { |
| // Ignore |
| } |
| throw throwable1; |
| } |
| } catch (Throwable throwable) { |
| LOGGER.warn("Error creating service for ManagedServiceFactory " + getName(), throwable); |
| } |
| } |
| } |
| |
| protected void postRegister(T t, Dictionary properties, ServiceRegistration registration) { |
| // Place holder |
| } |
| |
| protected void preUnregister(T t, Dictionary properties, ServiceRegistration registration) { |
| // Place holder |
| } |
| |
| private void internalDelete(String pid, int code) { |
| Pair<T, ServiceRegistration> pair = services.remove(pid); |
| if (pair != null) { |
| registrations.remove(pair.getSecond()); |
| Dictionary properties = JavaUtils.getProperties(pair.getSecond().getReference()); |
| try { |
| preUnregister(pair.getFirst(), properties, pair.getSecond()); |
| pair.getSecond().unregister(); |
| } catch (Throwable t) { |
| LOGGER.info("Error unregistering service", t); |
| } |
| try { |
| doDestroy(pair.getFirst(), properties, code); |
| } catch (Throwable t) { |
| LOGGER.info("Error destroying service", t); |
| } |
| } |
| } |
| |
| public void destroy() { |
| if (destroyed.compareAndSet(false, true)) { |
| executor.shutdown(); |
| try { |
| executor.awaitTermination(timeoutBeforeInterrupt, TimeUnit.MILLISECONDS); |
| } catch (InterruptedException e) { |
| throw new RuntimeException("Shutdown interrupted"); |
| } |
| if (!executor.isTerminated()) { |
| executor.shutdownNow(); |
| try { |
| executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); |
| } catch (InterruptedException e) { |
| throw new RuntimeException("Shutdown interrupted"); |
| } |
| } |
| |
| while (!services.isEmpty()) { |
| String pid = services.keySet().iterator().next(); |
| internalDelete(pid, BUNDLE_STOPPING); |
| } |
| } |
| } |
| |
| static class Pair<U,V> { |
| private U first; |
| private V second; |
| public Pair(U first, V second) { |
| this.first = first; |
| this.second = second; |
| } |
| public U getFirst() { |
| return first; |
| } |
| public V getSecond() { |
| return second; |
| } |
| public void setFirst(U first) { |
| this.first = first; |
| } |
| public void setSecond(V second) { |
| this.second = second; |
| } |
| } |
| |
| } |