/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.container; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * Synchronized type-safe map for loaded components. * Keys are component handles, values are <code>ComponentAdapter</code>s. * The map entries are sorted by insertion order, or by the handle sequence given to {@link #sort(int[])}. * <p> * Note that it is not sufficient to use something like * <code>Collections.synchronizedMap(new HashMap())</code>, * since reading out the keys or values of such a map with an iterator * is still backed by the original instance and therefore not thread-safe. * See {@link Collections#synchronizedMap(java.util.Map)}. * * @author hsommer * created Oct 30, 2003 10:04:28 AM */ class ComponentMap { /** * The map that backs this class. */ private LinkedHashMap<Integer, ComponentAdapter> m_map; private Logger logger; ComponentMap(Logger logger) { this.logger = logger; m_map = new LinkedHashMap<Integer, ComponentAdapter>(); } /** * Makes a reservation for a <code>ComponentAdapter</code> based on the component handle. * Can be used to avoid concurrent multiple activation of the same component (which also manager should prevent). * <p> * @TODO: There seems to be an error case where the manager sends a second activation request for the same component curl, * but using a different component handle. This seems to happen after the first activation request timed out * from the point of view of the manager, but continues to run inside the container. * See http://jira.alma.cl/browse/COMP-1863. * @param compHandle * @return true if this handle could be reserved */ synchronized boolean reserveComponent(int compHandle) { boolean ret = false; Integer handleObj = new Integer(compHandle); // need to check if it's an old ComponentAdapter we want to overwrite, or a reservation (null) if (!m_map.containsKey(handleObj) || m_map.get(handleObj) != null ) { m_map.put(handleObj, null); ret = true; } return ret; } /** * * @param compHandle * @param compAdapter * @return previously stored adapter, possibly null * @see Map#put(java.lang.Object, java.lang.Object) */ synchronized ComponentAdapter put(int compHandle, ComponentAdapter compAdapter) { return ( m_map.put(compHandle, compAdapter) ); } /** * Sorts the entries in the order given by <code>sortedHandles</code>. * The sorted component handles will be received from the manager. * Before this method is called, the iteration order is that of insertion order. * <p> * @param sortedHandles */ synchronized void sort(int[] sortedHandles) { Map<Integer, ComponentAdapter> tmpMap = new LinkedHashMap<Integer, ComponentAdapter>(m_map.size()); try { for (int i = 0; i < sortedHandles.length; i++) { ComponentAdapter compAdapter = get(sortedHandles[i]); if (compAdapter != null) { Integer keyObj = new Integer(sortedHandles[i]); tmpMap.put(keyObj, compAdapter); m_map.remove(keyObj); } else { logger.warning("Invalid component handle '" + sortedHandles[i] + "' provided for component sorting. " + "This means that ACS manager wrongly believes this container hosts a component with this handle."); } } if (!m_map.isEmpty()) { String missingHandles = ""; for (int handle : m_map.keySet()) { missingHandles += handle + " "; } logger.info("Not enough component handles provided for sorting. Missing handles: " + missingHandles); } } catch (Exception ex) { // we don't expect an exception here, but really don't want this sorting to disturb anything else logger.log(Level.WARNING, "Failed to sort components into correct shutdown order!", ex); } finally { m_map.putAll(tmpMap); } } /** * Removes a component adapter from the map. * * @param compHandle handle of component (adapter) to be removed * @return old <code>ComponentAdapter</code> with that handle, or null * @see Map#remove(java.lang.Object) */ synchronized ComponentAdapter remove(int compHandle) { return m_map.remove(new Integer(compHandle)); } /** * Returns the component adapter for a given component handle, * or <code>null</code> if either the component handle is unknown, or if the corresponding * component is still under construction (which means that the handle is just for a 'reservation'. * * @param compHandle * @return component adapter, or null * @see Map#get(java.lang.Object) */ synchronized ComponentAdapter get(int compHandle) { return m_map.get(new Integer(compHandle)); } /** * Gets component adapters with the specified handles out of the map. * Ignores handles for which no components are stored. * The order of the returned component adapters matches the order of the handles. * <p> * Unlike {@link Map#values()}, the returned array contains * a snapshot of the map contents, * rather than a view backed by the map. * * @param compHandles handles of those components whose adapters should be returned. * @return adapters for all components in the map whose handles match <code>compHandles</code>. * Possibly an empty array (if compHandles is null or empty or contains wrong handles), but never null. * @see Map#values() */ synchronized ComponentAdapter[] getComponentAdapters(int[] compHandles) { List<ComponentAdapter> adapters = new ArrayList<ComponentAdapter>(); if (compHandles != null) { for (int i = 0; i < compHandles.length; i++) { ComponentAdapter adapter = get(compHandles[i]); if (adapter != null) { adapters.add(adapter); } } } ComponentAdapter[] ret = adapters.toArray(new ComponentAdapter[adapters.size()]); return ret; } /** * Tries to find a component with the given name and type, regardless of the handle * which is the "primary key". * * @param name component instance name (curl) * @param type IDL type name * @return the component adapter that matches name and type, or null if none is found. */ ComponentAdapter getComponentByNameAndType(String name, String type) { // todo-: check performance; perhaps iterate directly over underlying map, then synchronized ComponentAdapter existingCompAdapter = null; ComponentAdapter[] compAdapters = getAllComponentAdapters(); for (int i = 0; i < compAdapters.length; i++) { ComponentAdapter otherAdapter = compAdapters[i]; if (otherAdapter.getName().equals(name) && otherAdapter.getType().equals(type)) { existingCompAdapter = otherAdapter; break; } } return existingCompAdapter; } /** * Gets all component adapters stored in the map. * Unlike {@link Map#values()}, the returned array contains a snapshot of the map contents, * rather than a live view backed by the map. * <p> * Notice that <code>null</code> adapters for {@link #reserveComponent(int) reserved} components * are not included in the returned array of adapters. * * @return adapters for all components in the map * @see Map#values() */ synchronized ComponentAdapter[] getAllComponentAdapters() { List<ComponentAdapter> nonNullAdapters = new ArrayList<ComponentAdapter>(); for (ComponentAdapter compAdapter : m_map.values()) { if (compAdapter != null) { nonNullAdapters.add(compAdapter); } } ComponentAdapter[] ret = nonNullAdapters.toArray(new ComponentAdapter[nonNullAdapters.size()]); return ret; } /** * Gets all component handles, which are the keys of the map. * Unlike {@link Map#keySet()}, the returned array contains a snapshot of the map contents, * rather than a view backed by the map. * * <p> * TODO: check if it's ok to include handles for reserved component adapters (value still null) * * @return keys for all component (adapters) in the map * @see Map#keySet() */ synchronized int[] getAllHandles() { Collection<Integer> keys = m_map.keySet(); int[] ret = new int[keys.size()]; int index=0; for (Integer key : keys) { ret[index] = key.intValue(); index++; } return ret; } }