esg.node.core.AbstractDataNodeManager.java Source code

Java tutorial

Introduction

Here is the source code for esg.node.core.AbstractDataNodeManager.java

Source

/***************************************************************************
 *                                                                          *
 *  Organization: Lawrence Livermore National Lab (LLNL)                    *
 *   Directorate: Computation                                               *
 *    Department: Computing Applications and Research                       *
 *      Division: S&T Global Security                                       *
 *        Matrix: Atmospheric, Earth and Energy Division                    *
 *       Program: PCMDI                                                     *
 *       Project: Earth Systems Grid (ESG) Data Node Software Stack         *
 *  First Author: Gavin M. Bell (gavin@llnl.gov)                            *
 *                                                                          *
 ****************************************************************************
 *                                                                          *
 *   Copyright (c) 2009, Lawrence Livermore National Security, LLC.         *
 *   Produced at the Lawrence Livermore National Laboratory                 *
 *   Written by: Gavin M. Bell (gavin@llnl.gov)                             *
 *   LLNL-CODE-420962                                                       *
 *                                                                          *
 *   All rights reserved. This file is part of the:                         *
 *   Earth System Grid (ESG) Data Node Software Stack, Version 1.0          *
 *                                                                          *
 *   For details, see http://esgf.org/esg-node/                             *
 *   Please also read this link                                             *
 *    http://esgf.org/LICENSE                                               *
 *                                                                          *
 *   * Redistribution and use in source and binary forms, with or           *
 *   without modification, are permitted provided that the following        *
 *   conditions are met:                                                    *
 *                                                                          *
 *   * Redistributions of source code must retain the above copyright       *
 *   notice, this list of conditions and the disclaimer below.              *
 *                                                                          *
 *   * Redistributions in binary form must reproduce the above copyright    *
 *   notice, this list of conditions and the disclaimer (as noted below)    *
 *   in the documentation and/or other materials provided with the          *
 *   distribution.                                                          *
 *                                                                          *
 *   Neither the name of the LLNS/LLNL nor the names of its contributors    *
 *   may be used to endorse or promote products derived from this           *
 *   software without specific prior written permission.                    *
 *                                                                          *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    *
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      *
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      *
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE    *
 *   LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR     *
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,           *
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT       *
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF       *
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND    *
 *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,     *
 *   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT     *
 *   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF     *
 *   SUCH DAMAGE.                                                           *
 *                                                                          *
 ***************************************************************************/

/**
   Description:
    
**/
package esg.node.core;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.*;

import java.util.Properties;
import java.util.Enumeration;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.regex.PatternSyntaxException;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;

//TODO: think about if we want this dependence on an outside package...
//may want to move the datasource managing to the ESGDataNodeManager subclass
//to keep this class prestine w.r.t. dependencies... think about it... -gmb
import esg.common.db.DatabaseResource;
import esg.common.util.ESGFProperties;

public abstract class AbstractDataNodeManager implements DataNodeManager {

    private static Log log = LogFactory.getLog(AbstractDataNodeManager.class);

    private Map<String, ESGPeer> peers = null;
    private Map<String, DataNodeComponent> components = null;
    private Map<String, Properties> propCache = null;
    private Properties props = null;
    private String myName = null;

    public AbstractDataNodeManager() {
        myName = "DN_MGR";
        //NOTE: May want to create these as either Synchronized Maps or ConcurrentHashMaps
        peers = new HashMap<String, ESGPeer>();
        components = new HashMap<String, DataNodeComponent>();
        propCache = new HashMap<String, Properties>();
        loadProperties();

        //NOTE: A quick lil short circuit to take database out of loop
        //so can test without having a database installed -gavin
        if (Boolean.valueOf(props.getProperty("esgf.test.no_db"))) {
            System.out.println("HEADS UP!!! Detected property esgf.test.no_db is "
                    + props.getProperty("esgf.test.no_db") + " so NOT initializing database resources");
            return;
        }

        //Parcel out the database properties... and setup database connection pool.
        DatabaseResource.init(props.getProperty("db.driver")).setupDataSource(props);
    }

    public abstract void init();

    public String getName() {
        return myName;
    }

    //-------------------------------------------
    //Property Loading and providing...
    //-------------------------------------------
    private void loadProperties() {
        log.trace("Loading Properties");
        InputStream in = null;
        try {
            propCache.clear();
            props = new ESGFProperties();
            log.trace("Properties of esgf.properties file: " + props);
            log.trace("Loaded " + props.size() + " Properties");
        } catch (FileNotFoundException ex) {
            log.error("Could not find esgf.properties file!!!");
        } catch (IOException ex) {
            log.error("Problem loading node's property file!", ex);
        } catch (NullPointerException ex) {
            log.error("PROPERTY FILE INPUT STREAM IS NULL!!", ex);
            throw ex;
        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (IOException ex) {
                log.error(ex);
            }
        }
    }

    public String getNodeProperty(String key) {
        return getNodeProperty(key, null);
    }

    public String getNodeProperty(String key, String defaultValue) {
        return props.getProperty(key, defaultValue);
    }

    //NOTE: the caching is a space vs speed trade off I think it is
    //cleaner to worry about such things (optimizations) at the root
    //of the issue than to make the interested parties (callers) worry
    //about how to parismoniously use this call. right? :-).  The
    //memory trade off is not so concrete since we are saving on "new"
    //property object creation, so if many calls are made it is
    //efficient.  If few calls are made, well... oh well... Just as
    //important, we are giving our callers less to worry about! :-)

    //Regex filtering the global properties by regex on keys
    public Properties getMatchingProperties(String regex) {
        log.trace("getting matching properties for [" + regex + "]");
        Properties matchProps = null;
        if ((matchProps = propCache.get(regex)) != null) {
            return matchProps;
        }
        matchProps = new Properties();
        String key = null;
        for (Enumeration keys = props.propertyNames(); keys.hasMoreElements();) {
            key = (String) keys.nextElement();
            //log.trace("inspecting: "+key);
            try {
                if (key.matches(regex)) {
                    //log.trace("matched: adding...");
                    matchProps.put(key, props.getProperty(key));
                }
            } catch (PatternSyntaxException ex) {
                log.error(ex.getMessage(), ex);
                break;
            }
        }
        propCache.put(regex, matchProps);
        log.trace("[" + regex + "] => (" + matchProps.size() + " entries)");
        log.trace("propCache size = " + propCache.size());
        return matchProps;
    }

    //-------------------------------------------
    //DataNodeManager Interface Implementations...
    //-------------------------------------------
    public boolean registerComponent(DataNodeComponent component) {
        if (component == null)
            return false;
        if ((component.getName() == null) || (component.getName().equals(DataNodeComponent.ANONYMOUS))) {
            log.warn("Will not register a component without a name... call setMyName(<name>)");
            return false;
        }

        log.trace("Registering Component: " + component.getName());
        if (component instanceof ESGPeer) {
            log.warn("WARNING: Will not register peer [" + component.getName() + "] as a component!");
            return false;
        }
        components.put(component.getName(), component);

        //Note: Casting because this method is not exposed by
        //interface but by the AbstractDataNodeComponent abstract
        //class)
        ((AbstractDataNodeComponent) component).setDataNodeManager(this);

        log.trace("Initializing newly registered component: " + component.getName());
        component.init();

        sendJoinNotification(component);
        component.addESGListener(this);
        return true;
    }

    public boolean hasComponent(String componentName) {
        return peers.containsKey(componentName);
    }

    public void removeComponent(String componentName) {
        DataNodeComponent component = components.remove(componentName);
        if (component == null) {
            log.trace("No component mapping to " + componentName + " (nothing to remove)");
            return;
        }
        log.trace("Removing Component: [" + componentName + "]");

        component.removeAllESGQueueListeners();
        component.removeAllESGListeners();
        sendUnjoinNotification(component);

        //TODO: add a call for the component to shut itself down
        //      accordingly.  Either here or in the component itself
        //      (see TODO comment in
        //      AbstractDataNodeComponent.unregister()).
    }

    //overloaded delegation of above
    public void removeComponent(DataNodeComponent component) {
        removeComponent(component.getName());
    }

    //TODO: Maybe think about the visibility of this method.
    public DataNodeComponent getComponent(String name) {
        return components.get(name);
    }

    public int numOfComponents() {
        return components.size();
    }

    public String[] getComponentNames() {
        return components.keySet().toArray(new String[] { "" });
    }

    //-------------------------------------------
    //The Peer Proxy object is a type of DataNodeComponent but with
    //special logic for handling it's connectivity and life cycle.  It
    //is treated separely here for more *semantic* reasons than
    //anything else.  I want to enforce the thinking that peer
    //stubs represent a different type of beast than "ordinary"
    //components.
    //-------------------------------------------
    public boolean registerPeer(ESGPeer peer) {
        if (peer == null)
            return false;
        log.trace("2)) Registering Peer in node manager: " + peer.getName());
        ((AbstractDataNodeComponent) peer).setDataNodeManager(this);
        log.trace("3)) Initializing newly registered peer component: " + peer.getName());
        peer.init();
        if (peer.isValid()) {
            peers.put(peer.getName(), peer);
            log.trace("5)) Sending Queued Join Notification...");
            sendQueuedJoinNotification(peer);
            peer.addESGListener(this);
            return true;
        }
        log.warn("Sorry Not Able To Register This Peer: " + peer);
        peers.remove(peer); //just to be extra extra sure it isn't there
        return false;
    }

    public boolean hasPeer(String peerName) {
        return peers.containsKey(peerName);
    }

    public void removePeer(String peerName) {
        ESGPeer peer = peers.remove(peerName);
        if (peer == null) {
            log.trace("No peer mapping to " + peerName + " (nothing to remove)");
            return;
        }
        log.trace("Removing Peer: [" + peerName + "]");
        peer.removeESGListener(this);
        sendQueuedUnjoinNotification(peer);
    }

    //overloaded delegation of above
    public void removePeer(ESGPeer peer) {
        removePeer(peer.getName());
    }

    //For communicating with a specific peer...
    public ESGPeer getPeer(String peerName) {
        return peers.get(peerName);
    }

    //For getting the list of peers...
    public List<ESGPeer> getPeers() {
        return Collections.unmodifiableList((List<ESGPeer>) peers.values());
    }

    public int numOfPeers() {
        return peers.size();
    }

    public String[] getPeerNames() {
        return peers.keySet().toArray(new String[] { "" });
    }

    //--------------------------------------------
    //The code the sews together the path of events through the system.
    //--------------------------------------------

    public void connect(String parentName, String childName) {
        log.info("Connecting: " + parentName + " -to-> " + childName);
        try {
            //For the moment there is a 1:1 with name and component.
            getComponent(parentName).addESGQueueListener(getComponent(childName));
        } catch (Exception e) {
            log.warn("Could not establish connection: " + parentName + " -to-> " + childName);
        }
    }

    //--------------------------------------------
    //ESGListener dispatch methods: to all registered ESGListeners
    //calling their handleESGEvent method
    //--------------------------------------------

    private void sendJoinNotification(DataNodeComponent component) {
        log.trace("Sending Join Notifications for: " + component.getName());
        ESGJoinEvent joinEvent = new ESGJoinEvent(this, component.getName(), component, ESGJoinEvent.JOIN);
        fireESGEvent(joinEvent);
    }

    private void sendUnjoinNotification(DataNodeComponent component) {
        log.trace("Sending UN-Join Notifications for :" + component.getName());
        ESGJoinEvent unjoinEvent = new ESGJoinEvent(this, component.getName(), component, ESGJoinEvent.UNJOIN);
        fireESGEvent(unjoinEvent);
    }

    //TODO think about the visibility of this method... was protected
    //but making public so can be called.  Have to trust that the caller
    //is truly only calling when things are indeed all done being loaded!!
    //revisit this issue!!
    public void sendAllLoadedNotification() {
        log.trace("Sending All-Loaded Notification Broadcast...");
        ESGSystemEvent allLoadedEvent = new ESGSystemEvent(this, ESGSystemEvent.ALL_LOADED);
        fireESGEvent(allLoadedEvent);
    }

    //TODO: Think about how I could get a subset view of the
    //subscription list to use for firing events to a subset of
    //entities that adhere to a particular interface.  I.E. get all
    //the objects in the list that implement the FooListener interface
    //for me to call back to.  In the mean time we push events to
    //everyone and let them deal with proper selection for handling

    protected void fireESGEvent(ESGEvent esgEvent) {
        Collection<? extends ESGListener> esgListeners = components.values();
        log.trace("Firing ESGEvent: " + esgEvent);
        for (ESGListener listener : esgListeners) {
            listener.handleESGEvent(esgEvent);
        }
    }

    //-------------------------------------------
    //ESGQueueListener Dispatch methods to: ESGQueueListeners
    //calling their handleESGQueueEvent method via enqueueEvent()
    //putting on on their own event handling thread!
    //-------------------------------------------

    private void sendQueuedJoinNotification(DataNodeComponent component) {
        log.trace("Sending Queued Join Notifications for: " + component.getName());
        ESGJoinEvent joinEvent = new ESGJoinEvent(this, component.getName(), component, ESGJoinEvent.JOIN);
        fireQueuedESGEvent(joinEvent);
    }

    private void sendQueuedUnjoinNotification(DataNodeComponent component) {
        log.trace("Sending Queued UN-Join Notifications for :" + component.getName());
        ESGJoinEvent unjoinEvent = new ESGJoinEvent(this, component.getName(), component, ESGJoinEvent.UNJOIN);
        fireQueuedESGEvent(unjoinEvent);
    }

    /**
       Puts the event on the event queues of all registered
       (listening) components.
     */
    protected void fireQueuedESGEvent(ESGEvent esgEvent) {
        Collection<? extends ESGQueueListener> esgListeners = components.values();
        log.trace("Firing ESGQueuedEvent: " + esgEvent);
        for (ESGQueueListener listener : esgListeners) {
            listener.getESGEventQueue().enqueueEvent(esgEvent);
        }
    }

    //-------------------------------------------
    //ESGListener Interface Implementation...
    //-------------------------------------------
    public void handleESGEvents(List<ESGEvent> events) {
        for (ESGEvent event : events) {
            handleESGEvent(event);
        }
    }

    public void handleESGEvent(ESGEvent event) {
        //TODO:
        //Just stubbed for now...
        log.debug("DNM: Got An Event!!!!: " + event + "\nmessage: " + event.getMessage());
    }

    //TODO: YES!!! this is horrendous, I will use string builder and
    //be a bit nicer of memory and time when cleaning up ;-)
    //Promise.... Seriously! :-)
    public String toString() {
        String[] names;
        String out = "";

        out += this.getClass().getName() + ":\n";
        out += "Number of Components: " + numOfComponents() + "\n";
        names = getComponentNames();
        for (String dncName : names) {
            out += "\t" + dncName + "\n";
        }
        out += "Number of Peers  : " + numOfPeers() + "\n";
        names = getPeerNames();
        for (String peerName : names) {
            out += "\t" + peerName + "\n";
        }
        return out;
    }

}