/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.catalog.util.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.TreeMap; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * This utility object sorts Services by their service rankings first and then breaks ties with * service ids. Its initial intent is to be used with {@link ddf.catalog.CatalogFramework} plugins, but can be * used with any service. * <p> * * <p> * It is an implementation of {@link List} but is backed by a sorted {@link TreeMap} of * <ServiceReference, T> where the {@link ServiceReference} objects are what is used to maintain the * list order but the objects passed by this {@link List} to clients are the actual service objects * and not the service references. * </p> * * <p> * For instance if this was a SortedServiceList<PreIngestPlugin> object, then in the internal * TreeMap the {@link ServiceReference} objects would be maintained as keys but * {@link ddf.catalog.plugin.PreIngestPlugin} objects would be what is passed to clients. Therefore, a call to a * populated {@link SortedServiceList} list such as <code>list.get(0)</code> would return the first * {@link ddf.catalog.plugin.PreIngestPlugin} object. * </p> * * @param <T> */ public class SortedServiceList<T> implements List<T> { private static final String READ_ONLY_ERROR_MESSAGE = "This list is meant to be read only."; private static final Logger LOGGER = LoggerFactory.getLogger(SortedServiceList.class); private Map<ServiceReference, T> serviceMap = Collections.synchronizedMap(new TreeMap<ServiceReference, T>(new ServiceComparator())); /** * Constructor accepting OSGi bundle context. This constructor is currently invoked by the * ddf-catalog-framework bundle's blueprint and the fanout-catalogframework bundle's blueprint * upon framework construction. * */ public SortedServiceList() { } protected BundleContext getContext() { Bundle cxfBundle = FrameworkUtil.getBundle(SortedServiceList.class); if (cxfBundle != null) { return cxfBundle.getBundleContext(); } return null; } /** * Adds the newly bound OSGi service and its service reference to the internally maintained and * sorted serviceMap. This method is invoked when a plugin is bound (created/installed). This * includes preingest, postingest, prequery, postquery, preresource, postresource plugins. * * @param ref * the OSGi service reference */ public void bindPlugin(ServiceReference ref) { LOGGER.debug(this + " Binding " + ref); BundleContext context = getContext(); if (context != null) { T service = (T) context.getService(ref); serviceMap.put(ref, service); } else { LOGGER.debug("BundleContext was null, unable to add service reference"); } LOGGER.debug(Arrays.asList(serviceMap.values()) .toString()); } /** * Removes the newly bound OSGi service and its service reference to the internally maintained * and sorted serviceMap. This method is invoked when a plugin is unbound (removed/uninstalled). * This includes preingest, postingest, prequery, postquery, preresource, postresource plugins. * * @param ref * the OSGi service reference */ public void unbindPlugin(ServiceReference ref) { LOGGER.debug("Unbinding {}", ref); serviceMap.remove(ref); LOGGER.debug(Arrays.asList(serviceMap.values()) .toString()); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public boolean add(T arg0) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public void add(int arg0, T arg1) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public boolean addAll(Collection<? extends T> arg0) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public boolean addAll(int arg0, Collection<? extends T> arg1) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public void clear() { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } @Override public boolean contains(Object arg0) { return serviceMap.containsValue(arg0); } @Override public boolean containsAll(Collection<?> arg0) { return serviceMap.values() .containsAll(arg0); } @Override public T get(int arg0) { LOGGER.debug("GET called on : {}", arg0); if (serviceMap.values() != null) { ArrayList<T> list = new ArrayList<T>(serviceMap.values()); return list.get(arg0); } return null; } @Override public int indexOf(Object arg0) { if (serviceMap.values() != null) { ArrayList<T> list = new ArrayList<T>(serviceMap.values()); return list.indexOf(arg0); } return -1; } @Override public boolean isEmpty() { return serviceMap.isEmpty(); } @Override public Iterator<T> iterator() { synchronized (serviceMap) { // Synchronizing on m, not s! return serviceMap.values() .iterator(); } } @Override public int lastIndexOf(Object arg0) { if (serviceMap.values() != null) { ArrayList<T> list = new ArrayList<T>(serviceMap.values()); return list.lastIndexOf(arg0); } return -1; } @Override public ListIterator<T> listIterator() { if (serviceMap.values() != null) { ArrayList<T> list = new ArrayList<T>(serviceMap.values()); return list.listIterator(); } return null; } @Override public ListIterator<T> listIterator(int arg0) { if (serviceMap.values() != null) { ArrayList<T> list = new ArrayList<T>(serviceMap.values()); return list.listIterator(arg0); } return null; } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public boolean remove(Object arg0) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public T remove(int arg0) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public boolean removeAll(Collection<?> arg0) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public boolean retainAll(Collection<?> arg0) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } /** * Unsupported operation, throws an UnsupportedOperationException since this list is read-only. */ @Override public T set(int arg0, T arg1) { throw new UnsupportedOperationException(READ_ONLY_ERROR_MESSAGE); } @Override public int size() { return serviceMap.size(); } @Override public List<T> subList(int arg0, int arg1) { if (serviceMap.values() != null) { ArrayList<T> list = new ArrayList<T>(serviceMap.values()); return list.subList(arg0, arg1); } return null; } @Override public Object[] toArray() { return serviceMap.values() .toArray(); } @Override public <T> T[] toArray(T[] arg0) { return serviceMap.values() .toArray(arg0); } }