/* * Created on 2004-jul-02 */ package com.jrockit.console.attribute.storage; import java.io.*; import java.util.*; import java.util.HashMap; import java.util.Map; import com.jrockit.console.attribute.AttributeSubscription; import com.jrockit.console.persistence.ColumnIdentifier; import com.jrockit.console.persistence.PairStorage; import com.jrockit.console.util.PreferencesKeys; /** * This class multiplexes between AttributeSubscriptions and PairStorages. The * subscriptions are provided by the console to the PersistingAttributeStorage. * They are grouped together by what machine / port they belong to. * * Each group has one PairStorage associated to it. * * @author johan */ public class AttributePairStorageMultiplexer { /** * Our singleton instance. */ private static AttributePairStorageMultiplexer m_defaultMultiplexer; /** * The conversion map. Keys are attribute group names and values are PairStorages. * * @see #getGroupName(AttributeSubscription) */ private Map m_group2pairStorage; /** * Who's keeping a certain PairStorage open? */ private Map> m_pairStorageUsers; /** * Who do the groups consist of? */ private Map> m_groupMembers; /** * This is a singleton, users aren't allowed to instantiate. * * @see #getDefaultMultiplexer() */ private AttributePairStorageMultiplexer() { m_group2pairStorage = new HashMap(); m_pairStorageUsers = new HashMap>(); m_groupMembers = new HashMap>(); } /** * @return the AttributePairStorageMultiplexer singleton. */ public synchronized static AttributePairStorageMultiplexer getDefaultMultiplexer() { if (m_defaultMultiplexer == null) { m_defaultMultiplexer = new AttributePairStorageMultiplexer(); } return m_defaultMultiplexer; } /** * @param attributeSubscription The AttributeSubscription to find the * PairStorage for. * * @return the PairStorage associated with this AttributeSubscription. */ public synchronized PairStorage getPairStorage(AttributeSubscription attributeSubscription) { // If this attributeSubscription already has a PairStorage, return that. PairStorage pairStorage = m_group2pairStorage.get(getGroupName(attributeSubscription)); if (pairStorage != null && m_pairStorageUsers.get(pairStorage).contains(attributeSubscription)) { return pairStorage; } // If this attributeSubscription hasn't been registered previously, we don't want to do this. if (!m_groupMembers.containsKey(getGroupName(attributeSubscription))) { throw new IllegalStateException(attributeSubscription + " must be registered through willWantPairStorage()"); } if (!m_groupMembers.get(getGroupName(attributeSubscription)).contains(attributeSubscription)) { throw new IllegalStateException(attributeSubscription + " must be registered through willWantPairStorage()"); } // If this attributeSubscription's group has a PairStorage, close that. if (pairStorage != null) { pairStorage.close(); m_group2pairStorage.remove(getGroupName(attributeSubscription)); m_pairStorageUsers.remove(pairStorage); pairStorage = null; } // Invariant: This subscription is registered but doesn't have a PairStorage try { // Open a new PairStorage for this attributeSubscription and // everything in its group File dataDirectory = getDataDirectory(); if (dataDirectory == null) { // FIXME: Handle this some other way System.err.println("Unable to open persistence directory"); System.exit(1); } pairStorage = new PairStorage( dataDirectory, "jrconsole", getColumnIdentifiers(getGroupName(attributeSubscription)), '\t', "csv"); } catch (FileNotFoundException e) { // FIXME: Handle this some better way e.printStackTrace(); System.exit(1); } // Remember the new PairStorage m_group2pairStorage.put(getGroupName(attributeSubscription), pairStorage); Set users = new HashSet(); m_pairStorageUsers.put(pairStorage, users); for (AttributeSubscription user : m_groupMembers.get(getGroupName(attributeSubscription))) { users.add(user); } return pairStorage; } /** * Fetch the group name from an AttributeSubscription. */ private static String getGroupName(AttributeSubscription attributeSubscription) { return attributeSubscription.getRJMXConnectorModel().getCompactName(); } /** * Convert the aspects with the given group name into a ColumnIdentifier * array. */ private ColumnIdentifier[] getColumnIdentifiers(String aspectGroupName) { ArrayList columnIdentifiers = new ArrayList(5); for (AttributeSubscription subscription : m_groupMembers.get(aspectGroupName)) { columnIdentifiers.add(subscription2columnIdentifier(subscription)); } return columnIdentifiers.toArray(new ColumnIdentifier[0]); } /** * Create a ColumnIdentifier from an AttributeSubscription. * * @param attributeSubscription * The AttributeSubscription to turn into a column identifier. * * @return A column identifier for the AttributeSubscription. */ private static ColumnIdentifier subscription2columnIdentifier( AttributeSubscription attributeSubscription) { String censoredName = attributeSubscription.getName().replace(' ', '-'); return new ColumnIdentifier(censoredName, attributeSubscription.getDescription()); } /** * Convert a subscription group name into a File representing a directory * where to store data about that subscription group. *

* Create the directory if it doesn't exist. * * @return null if the directory doesn't exist and can't be created */ private static File getDataDirectory() { String settingsDirectory = System.getProperty( PreferencesKeys.PROPERTY_PERSISTENCE_PATH, PreferencesKeys.DEFAULT_PERSISTENCE_PATH); File returnMe = new File(settingsDirectory); if (returnMe.exists() && !returnMe.isDirectory()) { // A file is in the way for our directory return null; } if (!returnMe.exists() && !returnMe.mkdirs()) { // Can't create the requested directory return null; } return returnMe; } /** * @param attribute The attribute to get a ColumnIdentifier for. * * @return a ColumnIdentifier matching the given AttributeSubscription. * * @throws IllegalStateException * if no PairStorage is associated with that aspect. */ public synchronized ColumnIdentifier getColumnIdentifier(AttributeSubscription attribute) { if (getPairStorage(attribute) == null) { throw new IllegalStateException("That subscription hasn't been registered using willWantPairStorage()"); } return getColumnIdentifier(attribute); } /** * Close the association between this aspect and the on disk backing * storage. * * @param attribute The connection to close. */ public synchronized void close(AttributeSubscription attribute) { PairStorage pairStorage = getPairStorage(attribute); Set users = m_pairStorageUsers.get(pairStorage); if (users == null || !users.contains(attribute)) { throw new IllegalArgumentException(attribute + " doesn't have a PairStorage open"); } // This attribute doesn't want the PairStorage any more users.remove(attribute); // Unless anybody else is interested... if (users.isEmpty()) { // ... close it and forget about it pairStorage.close(); m_pairStorageUsers.remove(pairStorage); } Set members = m_groupMembers.get(getGroupName(attribute)); assert(members != null); members.remove(attribute); if (members.isEmpty()) { m_groupMembers.remove(getGroupName(attribute)); } } /** * This method registers that the persistence layer will be required to * handle a certain Aspect. * * @param subscription The subscription that will want to talk to a PairStorage. */ public synchronized void willWantPairStorage(AttributeSubscription subscription) { String groupName = getGroupName(subscription); // FIXME: Update m_groupMembers if (m_groupMembers.get(groupName).contains(subscription)) { // Already registered, our work here is done return; } m_groupMembers.get(groupName).add(subscription); // FIXME: Update m_group2pairStorage // FIXME: If this group has a non-null PairStorage, close it and // forget about it if (m_group2pairStorage.get(groupName) // FIXME: Update m_pairStorageUsers if (m_group2pairStorage.containsKey(groupName)) { // Already registered, do nothing return; } // If other subscriptions in the same group actually have a PairStorage // associated with them... PairStorage pairStorage = m_group2pairStorage.get(groupName); if (pairStorage != null) { // ... close that PairStorage and forget about it pairStorage.close(); m_group2pairStorage.remove(groupName); } // Invariant: The subscription isn't in our map // Mark this group as not yet having a PairStorage m_group2pairStorage.put(groupName, null); } }