esg.node.components.registry.RegistrationGleaner.java Source code

Java tutorial

Introduction

Here is the source code for esg.node.components.registry.RegistrationGleaner.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 Federation (ESGF) Data Node Software   *
*  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 Federation (ESGF) Data Node Software Stack           *
*                                                                          *
*   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.                                                           *
*                                                                          *
***************************************************************************/
package esg.node.components.registry;

import esg.common.generated.registration.*;
import esg.common.util.ESGFProperties;
import esg.common.QuickHash;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Date;
import java.util.Properties;
import java.util.HashMap;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import javax.xml.transform.stream.StreamSource;

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

import static esg.node.components.registry.NodeTypes.*;

/**
   Description:
    
   Encapsulates the logic for fetching and generating this local
   node's registration form, as defined by the registration.xsd.  (The
   LasSistersGleaner is also used to further create the
   las_servers.xml derivative file.)
    
*/
public class RegistrationGleaner {

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

    //NOTE: IF OTHER STATES BECOME APPARENT MAKE AN ENUM...
    public static final String NOT_AVAILABLE = "NOT_AVAILABLE";

    //NOTE: Content of metrics file looks like: DOWNLOADCOUNT=0,DOWNLOADSIZE=0,DOWNLOADUSERS=5,REGISTEREDUSERS=20
    private static final String DATA_METRICS_FILENAME = "data_users.metrics";
    private static final String dashboardMetricsFileRegex = "DOWNLOADCOUNT=([0-9]*),DOWNLOADSIZE=([0-9]*),DOWNLOADUSERS=([0-9]*)(?:,REGISTEREDUSERS=([0-9]*))*$";
    private static final Pattern dashboardMetricsFilePattern = Pattern.compile(dashboardMetricsFileRegex,
            Pattern.CASE_INSENSITIVE);
    private Matcher dashboardMetricsMatcher = null;

    private static final String registrationFile = "registration.xml";
    private static final boolean DEBUG = true;
    private String registrationPath = null;
    private Registration myRegistration = null;
    private HashMap<String, Node> myNodeMap = null;
    private QuickHash quickHash = null;
    private String myChecksum = null;
    private Properties props = null;
    private RegistrationGleanerHelperDAO helperDAO = null;
    private String configDir = null;
    private String nodeTypeValue = "-1"; //TODO: yes, yes... turn this into enums strings in xsd - later.
    private boolean dirty = false;

    public RegistrationGleaner() {
        this(null);
    }

    public RegistrationGleaner(Properties props) {
        this.props = props;
        this.init();
    }

    public void init() {
        try {
            if (props == null)
                this.props = new ESGFProperties();
        } catch (Exception e) {
            log.error(e);
        }
        registrationPath = props.getProperty("node.manager.service.app.home", ".") + File.separator;
        readMyNodeType();
    }

    /**
       The node type value returned is a string of an int
       that represents a bit vector corresponding to an installation type
       see the esg-node install script for the values.
       <p>
       DATA_BIT=4<br>
       INDEX_BIT=8<br>
       IDP_BIT=16<br>
       COMPUTE_BIT=32<br>
    */
    public String getMyNodeType() {
        return nodeTypeValue;
    }

    /**
       Reads the node type from the configuration file that contains it
     */
    private String readMyNodeType() {
        nodeTypeValue = null;
        if (null != (configDir = System.getenv().get("ESGF_HOME"))) {
            configDir = configDir + File.separator + "config";
            try {
                quickHash = new QuickHash("SHA1");
                File configTypeFile = new File(configDir + File.separator + "config_type");
                if (configTypeFile.exists()) {
                    BufferedReader in = new BufferedReader(new FileReader(configTypeFile));
                    try {
                        nodeTypeValue = in.readLine().trim();
                        log.trace("node type = " + nodeTypeValue);
                    } catch (java.io.IOException ex) {
                        log.error(ex);
                    } finally {
                        if (null != in)
                            in.close();
                    }
                }
            } catch (Throwable t) {
                log.error(t);
            }
        }
        return nodeTypeValue;
    }

    public Registration getMyRegistration() {
        return myRegistration;
    }

    public String getMyChecksum() {
        return myChecksum;
    }

    public synchronized boolean saveRegistration() {
        return saveRegistration(myRegistration, false);
    }

    public synchronized boolean saveRegistration(boolean doChecks) {
        return saveRegistration(myRegistration, doChecks);
    }

    public synchronized boolean saveRegistration(Registration registration) {
        sync();
        return saveRegistration(myRegistration, false);
    }

    public synchronized boolean saveRegistration(Registration registration, boolean doChecks) {
        boolean success = false;
        if (registration == null) {
            log.error("Registration is null ? [" + registration + "]");
            return success;
        }
        log.info("Saving registration information to " + registrationPath + this.registrationFile);
        try {
            JAXBContext jc = JAXBContext.newInstance(Registration.class);
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.marshal(registration, new FileOutputStream(registrationPath + this.registrationFile));
            success = true;
            dirty = false;
        } catch (Exception e) {
            log.error(e);
        }

        //-----------------------------------------------
        //Derivative xml file generation... (NOTE: should be refactored, but in brody mode right now)
        //-----------------------------------------------

        //pull from registry to create las sisters file.
        try {
            String endpoint = null;
            if ((null != (endpoint = props.getProperty("las.service.endpoint")))
                    && (new File(props.getProperty("las.service.app.home"))).exists()) {
                LasSistersGleaner lasSisterGleaner = new LasSistersGleaner(props);
                log.trace("My LAS endpoint = [" + endpoint + "]");
                log.trace("Current Registration = [" + registration + "]");
                log.trace("registration has (" + registration.getNode().size() + ") nodes");
                log.trace("lasSisterGleaner=" + lasSisterGleaner);
                lasSisterGleaner.appendToMyLasServersFromRegistration(registration).saveLasServers();
            } else {
                log.warn("Could not get las information to save for some reason");
            }
        } catch (Exception e) {
            log.error(e);
            log.trace("props=" + props);
            if (log.isTraceEnabled())
                e.printStackTrace();
        }

        //pull from registry to create idp whilelist file.
        try {
            IdpWhitelistGleaner idpWhitelistGleaner = new IdpWhitelistGleaner(props);
            log.trace("registration=" + registration);
            log.trace("idpWhitelistGleaner=" + idpWhitelistGleaner);
            idpWhitelistGleaner.appendToMyIdpWhitelistFromRegistration(registration).saveIdpWhitelist();
        } catch (Exception e) {
            log.error(e);
            log.trace("props=" + props);
            if (log.isTraceEnabled())
                e.printStackTrace();
        }

        //pull from registry to create azs whilelist file.
        try {
            AzsWhitelistGleaner azsWhitelistGleaner = new AzsWhitelistGleaner(props);
            log.trace("registration=" + registration);
            log.trace("azsWhitelistGleaner=" + azsWhitelistGleaner);
            azsWhitelistGleaner.appendToMyAzsWhitelistFromRegistration(registration).saveAzsWhitelist();
        } catch (Exception e) {
            log.error(e);
            log.trace("props=" + props);
            if (log.isTraceEnabled())
                e.printStackTrace();
        }

        //pull from registry to create ats whilelist file.
        try {
            AtsWhitelistGleaner atsWhitelistGleaner = new AtsWhitelistGleaner(props);
            log.trace("registration=" + registration);
            log.trace("atsWhitelistGleaner=" + atsWhitelistGleaner);
            atsWhitelistGleaner.appendToMyAtsWhitelistFromRegistration(registration).saveAtsWhitelist();
        } catch (Exception e) {
            log.error(e);
            log.trace("props=" + props);
            if (log.isTraceEnabled())
                e.printStackTrace();
        }

        //pull from registry to create index shards list file.
        try {
            ShardsListGleaner shardsListGleaner = new ShardsListGleaner(props);
            log.trace("registration=" + registration);
            log.trace("shardsListGleaner=" + shardsListGleaner);
            shardsListGleaner.appendToMyShardsListFromRegistration(registration, doChecks).saveShardsList();
        } catch (Exception e) {
            log.error(e);
            log.trace("props=" + props);
            if (log.isTraceEnabled())
                e.printStackTrace();
        }

        return success;
    }

    //NOTE: In anticipation that checksumming for every call to
    //toString maybe a bit laborious and slow I am breaking up this
    //call into a "regular" toString and on that does the checksum
    //calcuation in addition to that. I don't want to optimize too
    //early, but the nature of toString and how frequenly it is
    //potentially called makes this a relatively justified defensive
    //maneuver, right? :-) The real thing I wanted to do was to put
    //this checksumming in the saveRegistration... however, currently
    //saveRegistration streams to the file and thus not holding the
    //whole file in memory, which for large files may be desireable.
    //However, we are not talking about huge files, Howevever, this
    //call could be done frequenly enough that constantly allocating
    //and gc'ing space for this string may make the JVM not so happy?
    //I am not sure, so I am leaving these options still open.  Time
    //vs. Space...  Thus... optimize this out later.... -gavin
    //public String toCheckedString() {
    //    String out = this.toString();
    //    if(out == null) return out;
    //
    //    myChecksum = quickHash.sum(sw.toString());
    //    log.trace("Checksum of xml string is: "+myChecksum);
    //    return out;
    //}
    //
    //public String toString() {
    //    StringWriter sw = null;
    //    if (myRegistration == null) {
    //        log.error("Registration is ["+myRegistration+"]");
    //        return null;
    //    }
    //    log.info("Writing registration information to String, for "+myRegistration.getNode().get(0).getHostname());
    //    sw = new StringWriter();
    //    try{
    //        JAXBContext jc = JAXBContext.newInstance(Registration.class);
    //        Marshaller m = jc.createMarshaller();
    //        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    //        m.marshal(myRegistration, sw);
    //    }catch(Exception e) {
    //        log.error(e);
    //    }
    //    return sw.toString();
    //}

    public String toString() {
        String out = null;
        if (myRegistration == null) {
            log.error("Registration is [" + myRegistration + "]");
            return null;
        }
        log.trace(
                "Writing registration information to String, for " + myRegistration.getNode().get(0).getHostname());
        try {
            StringWriter sw = new StringWriter();
            JAXBContext jc = JAXBContext.newInstance(Registration.class);
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.marshal(myRegistration, sw);

            out = sw.toString();
            sync();
            //NOTE: Hopefully this isn't to slow... may have to move
            //to saveRegistration since that would potentially be
            //called much less often
            log.trace("Checksumming xml content...");
            String lastChecksum = myChecksum;
            myChecksum = quickHash.sum(sw.toString());
            dirty = (!myChecksum.equals(lastChecksum));
            log.debug("Checksum of xml string is: " + myChecksum + (dirty ? " (modified)" : " (unchanged"));
        } catch (Exception e) {
            log.error(e);
        }
        return out;
    }

    public boolean isDirty() {
        return dirty;
    }

    /**
       Looks through the current system and gathers the configured
       node service information.  Takes that information and
       creates a local representation of this node's registration.
    */
    public synchronized RegistrationGleaner createMyRegistration() {
        log.info("Creating my registration representation...");
        String endpointDir = null;
        String endpoint = null;
        Node node = new Node();
        long timestamp = (new Date()).getTime();
        int nodeTypeInt = 0;

        try {
            String nodeHostname = props.getProperty("esgf.host");

            //************************************************
            //CORE
            //************************************************

            //Query the user for...
            node.setOrganization(props.getProperty("esg.root.id"));
            node.setLongName(props.getProperty("node.long.name"));
            node.setShortName(props.getProperty("node.short.name"));
            node.setSupportEmail(props.getProperty("mail.admin.address"));
            node.setNamespace(props.getProperty("node.namespace"));
            node.setNodePeerGroup(props.getProperty("node.peer.group"));

            //Pulled from system or install
            node.setHostname(nodeHostname);
            node.setIp(props.getProperty("esgf.host.ip"));
            node.setDn(props.getProperty("node.dn")); //zoiks
            node.setNamespace(props.getProperty("node.namespace")); //zoiks
            node.setTimeStamp(timestamp);
            node.setVersion(props.getProperty("version"));
            node.setRelease(props.getProperty("release"));
            node.setNodeType(readMyNodeType());
            node.setDefaultPeer(props.getProperty("esgf.default.peer", "pcmdi9.llnl.gov"));
            node.setAdminPeer(props.getProperty("myproxy.endpoint").split(":", 2)[0]); //remove port if present

            nodeTypeInt = Integer.parseInt(nodeTypeValue);

            //What is this ?
            CA ca = new CA();
            ca.setEndpoint(props.getProperty("esgf.host", "dunno"));
            ca.setHash(props.getProperty("security.ca.hash", "dunno"));
            ca.setDn(props.getProperty("security.ca.dn", "dunno"));
            node.setCA(ca);

            try {
                if ((null != (endpoint = props.getProperty("node.manager.service.endpoint")))
                        && (new File(props.getProperty("node.manager.service.app.home"))).exists()) {
                    NodeManager nodeManager = new NodeManager();
                    nodeManager.setEndpoint(endpoint);
                    node.setNodeManager(nodeManager);
                }
            } catch (Throwable t) {
                log.error(t);
            }

            //------------------------------------------------
            //GEO LOCATION Information (used by dashboard)
            //------------------------------------------------

            try {
                String geoLat = null;
                String geoLon = null;
                String city = null;

                //You must at very least have lat and lon set to even set up this entry
                if ((null != (geoLat = props.getProperty("node.geolocation.lat")))
                        && (null != (geoLon = props.getProperty("node.geolocation.lon")))) {

                    GeoLocation geoLocation = new GeoLocation();
                    geoLocation.setLat(geoLat);
                    geoLocation.setLon(geoLon);

                    if (null != (city = props.getProperty("node.geolocation.city")))
                        geoLocation.setCity(city);

                    node.setGeoLocation(geoLocation);
                }
            } catch (Throwable t) {
                log.error(t);
            }

            //************************************************
            //Data
            //************************************************
            if ((nodeTypeInt & DATA_BIT) != 0) {
                try {
                    if ((null != (endpoint = props.getProperty("thredds.service.endpoint")))
                            && (new File(props.getProperty("thredds.service.app.home"))).exists()) {
                        ThreddsService tds = new ThreddsService();
                        tds.setEndpoint(endpoint);
                        node.setThreddsService(tds);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                try {
                    if ((null != (endpoint = props.getProperty("orp.service.endpoint")))
                            && (new File(props.getProperty("orp.service.app.home"))).exists()) {
                        RelyingPartyService orp = new RelyingPartyService();
                        orp.setEndpoint(endpoint);
                        node.setRelyingPartyService(orp);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                //security (authorization service)
                try {
                    if ((null != (endpoint = props.getProperty("orp.security.authorization.service.endpoint")))
                            && (new File(props.getProperty("orp.security.authorization.service.app.home")))
                                    .exists()) {
                        AuthorizationService authzSvc = new AuthorizationService();
                        authzSvc.setEndpoint(endpoint);
                        node.setAuthorizationService(authzSvc);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                //------------------------------------------------
                //GLOBUS SUPPORT TOOLS (GridFTP)
                //------------------------------------------------

                try {
                    if ((null != (endpoint = props.getProperty("gridftp.endpoint")))
                            && (new File(props.getProperty("gridftp.app.home"))).exists()) {
                        GridFTPService gftp = new GridFTPService();
                        gftp.setEndpoint(endpoint);

                        //GridFTPServiceType.REPLICATION (BDM)
                        //GridFTPServiceType.DOWNLOAD (END-USER)
                        String configLabels = null;
                        if (null != (configLabels = props.getProperty("gridftp.config"))) {
                            for (String configLabel : configLabels.split("\\s+")) {
                                Configuration gftpConfig = new Configuration();
                                if (configLabel.equalsIgnoreCase("bdm")) {
                                    gftpConfig.setServiceType(GridFTPServiceType.REPLICATION);
                                    gftpConfig.setPort(props.getProperty("gridftp.bdm.server.port", "2812"));
                                }
                                if (configLabel.equalsIgnoreCase("end-user")) {
                                    gftpConfig.setServiceType(GridFTPServiceType.DOWNLOAD);
                                    gftpConfig.setPort(props.getProperty("gridftp.server.port", "2811"));//(standard gsiftp port)
                                }
                                gftp.getConfiguration().add(gftpConfig);
                            }
                        }

                        node.setGridFTPService(gftp);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                PEMCert cert = new PEMCert();
                cert.setCert(fetchMyPemCert());
                node.setPEMCert(cert);

                //For download metrics information (provided by dashboard's information provider)
                File dataUsersMetricsFile = null;
                if ((dataUsersMetricsFile = new File(props.getProperty("dashboard.ip.app.home", "/tmp/asvcsesa")
                        + File.separator + DATA_METRICS_FILENAME)).exists()) {
                    String dataUsersMetricsContent = null;

                    //----------------------------------------
                    //NOTE:
                    // As an optimization... should watch the file for
                    // updated content and only then read the file's
                    // contents.  Right now there is a lot of "new"s
                    // and operations as this gets called for every
                    // generation of the local registration.xml
                    // -gavin
                    //----------------------------------------

                    try {
                        BufferedReader in = new BufferedReader(new FileReader(dataUsersMetricsFile));
                        try {
                            dataUsersMetricsContent = in.readLine().trim();
                            log.trace(dataUsersMetricsFile.getAbsolutePath() + " : " + dataUsersMetricsContent);
                        } catch (java.io.IOException ex) {
                            log.error(ex);
                        } finally {
                            if (null != in)
                                in.close();
                        }
                    } catch (Throwable t) {
                        log.error(t);
                    }

                    if (dataUsersMetricsContent != null && !dataUsersMetricsContent.isEmpty()) {
                        Matcher m = dashboardMetricsFilePattern.matcher(dataUsersMetricsContent);
                        String downloadCount = null;
                        String downloadSize = null;
                        String downloadUsers = null;
                        String registeredUserCount = null;
                        if (m.find()) {
                            downloadCount = m.group(1);
                            downloadSize = m.group(2);
                            downloadUsers = m.group(3);
                            registeredUserCount = m.group(4);

                            Metrics metrics = new Metrics();
                            DownloadedData downloadedData = new DownloadedData();
                            downloadedData.setCount(downloadCount); //value of "DOWNLOADCOUNT"
                            downloadedData.setSize(downloadSize); //value of "DOWNLOADSIZE"
                            downloadedData.setUsers(downloadUsers); //value of "DOWNLOADUSERS"
                            metrics.setDownloadedData(downloadedData);

                            //The value of registered users is optional...
                            if (registeredUserCount != null || registeredUserCount.trim().equals("0")) {
                                RegisteredUsers registeredUsers = new RegisteredUsers();
                                registeredUsers.setCount(registeredUserCount);//value of "REGISTEREDUSERS"
                                metrics.setRegisteredUsers(registeredUsers);
                            }
                            node.setMetrics(metrics);
                        }
                    }
                }

            }

            //************************************************
            //COMPUTE
            //************************************************
            if ((nodeTypeInt & COMPUTE_BIT) != 0) {
                try {
                    if ((null != (endpoint = props.getProperty("las.service.endpoint")))
                            && (new File(props.getProperty("las.service.app.home"))).exists()) {
                        LASService las = new LASService();
                        log.trace("Setting LAS endpoint to " + endpoint);
                        las.setEndpoint(endpoint);
                        log.trace("Setting LAS service to " + las);
                        node.setLASService(las);
                    } else {
                        log.trace("Could not set las information in node [" + node + "]");
                    }
                } catch (Throwable t) {
                    log.error(t);
                }
            }

            //************************************************
            //IDP (security)
            //************************************************
            if ((nodeTypeInt & IDP_BIT) != 0) {

                //esgf-idp
                try {
                    if ((null != (endpoint = props.getProperty("idp.service.endpoint")))
                            && (new File(props.getProperty("idp.service.app.home"))).exists()) {
                        OpenIDProvider openid = new OpenIDProvider();
                        openid.setEndpoint(endpoint);
                        node.setOpenIDProvider(openid);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                //security (attribute service)
                try {
                    if ((null != (endpoint = props.getProperty("idp.security.attribute.service.endpoint")))
                            && (new File(props.getProperty("idp.security.attribute.service.app.home"))).exists()) {
                        AttributeService attrSvc = new AttributeService();
                        attrSvc.setEndpoint(endpoint);
                        loadAttributeServiceGroups(attrSvc);
                        node.setAttributeService(attrSvc);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                //security (registration service)
                try {
                    if ((null != (endpoint = props.getProperty("idp.security.registration.service.endpoint")))
                            && (new File(props.getProperty("idp.security.registration.service.app.home")))
                                    .exists()) {
                        RegistrationService regSvc = new RegistrationService();
                        regSvc.setEndpoint(endpoint);
                        node.setRegistrationService(regSvc);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                //------------------------------------------------
                //GLOBUS SUPPORT TOOLS (MyProxy)
                //------------------------------------------------

                try {
                    if ((null != (endpoint = props.getProperty("myproxy.endpoint")))
                            && (new File(props.getProperty("myproxy.app.home"))).exists()) {
                        MyProxyService mproxy = new MyProxyService();
                        mproxy.setEndpoint(endpoint);
                        mproxy.setDn(props.getProperty("mproxy.dn"));
                        node.setMyProxyService(mproxy);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

            }

            //************************************************
            //INDEX (search)
            //************************************************
            if ((nodeTypeInt & INDEX_BIT) != 0) {
                try {
                    if ((null != (endpoint = props.getProperty("index.service.endpoint")))
                            && (new File(props.getProperty("index.service.app.home"))).exists()) {
                        IndexService idx = new IndexService();
                        idx.setEndpoint(endpoint);
                        idx.setPort(props.getProperty("index.slave.port", "8983"));
                        node.setIndexService(idx);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                try {
                    if ((null != (endpoint = props.getProperty("publishing.service.endpoint")))
                            && (new File(props.getProperty("publishing.service.app.home"))).exists()) {
                        PublishingService pub = new PublishingService();
                        pub.setEndpoint(endpoint);
                        node.setPublishingService(pub);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }

                try {
                    String title = null;
                    String host = null;
                    if ((null != (title = props.getProperty("esgf.feed.datasets.title")))
                            && (null != (host = props.getProperty("esgf.host")))) {
                        RSSFeeds feeds = new RSSFeeds();
                        RSSFeed feed = new RSSFeed();
                        feed.setTitle(title);
                        feed.setUrl("http://" + host + "/esg-search/feed/node.rss");
                        feeds.getRSSFeed().add(feed);
                        node.setRSSFeeds(feeds);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }
            }

            //************************************************
            //Web Front-End
            //************************************************
            if ((nodeTypeInt & INDEX_BIT) != 0) {

                //esgf-web-fe
                try {
                    if ((null != (endpoint = props.getProperty("web.fe.service.endpoint")))
                            && (new File(props.getProperty("web.fe.service.app.home"))).exists()) {
                        FrontEnd webfe = new FrontEnd();
                        webfe.setEndpoint(endpoint);
                        node.setFrontEnd(webfe);
                    }
                } catch (Throwable t) {
                    log.error(t);
                }
            }

            //************************************************
            //INDEX DEPRECATED SERVICE
            //************************************************
            //OAIRepository oaiRepo = new OAIRepository();
            //oaiRepo.setEndpoint("oairepo-endpoint");
            //node.setOAIRepository(oaiRepo);         

        } catch (Exception e) {
            log.error(e);
        }

        if (null == myRegistration)
            myRegistration = new Registration();
        myRegistration.getNode().add(node);
        myRegistration.setVersion(ESGFRegistry.PROTOCOL_VERSION);
        myRegistration.setTimeStamp(timestamp); //touch'ing the registration...
        return this;
    }

    private String fetchMyPemCert() {
        log.trace("Fetching PEM Certificate...");

        byte[] buffer = null;
        java.io.BufferedInputStream f = null;

        try {

            String certfilename = "/etc/grid-security/hostcert.pem";
            File certfile = new File(certfilename);
            if (!certfile.exists()) {
                log.warn("Could not find " + certfile.getCanonicalPath());
                return NOT_AVAILABLE;
            }

            //NOTE: should do this with NIO buffers and specify the right charset
            //so that the buffer size is accurate... put on list of TOODs -gavin
            buffer = new byte[(int) certfile.length()];
            f = new java.io.BufferedInputStream(new java.io.FileInputStream(certfile));
            f.read(buffer);
        } catch (java.io.IOException ex) {
            log.error(ex);
            buffer = new byte[0];
        } finally {
            if (f != null)
                try {
                    f.close();
                } catch (java.io.IOException ignored) {
                }
        }
        log.trace("Certificate Fetched!");
        return new String(buffer);
    }

    //NOTE: May have to synchronized his method...
    public void sync() {
        log.trace("sync'ing...");
        if (null == myRegistration)
            return;
        if (null == myNodeMap)
            myNodeMap = new HashMap<String, Node>();
        myNodeMap.clear();
        for (Node node : myRegistration.getNode()) {
            myNodeMap.put(node.getHostname(), node);
        }
    }

    public synchronized RegistrationGleaner loadMyRegistration() throws ESGFRegistryException {
        return loadMyRegistration(registrationPath + this.registrationFile);
    }

    public synchronized RegistrationGleaner loadMyRegistration(String filename) throws ESGFRegistryException {
        log.info("Loading my registration info from " + filename);
        try {
            JAXBContext jc = JAXBContext.newInstance(Registration.class);
            Unmarshaller u = jc.createUnmarshaller();
            JAXBElement<Registration> root = u.unmarshal(new StreamSource(new File(filename)), Registration.class);
            myRegistration = root.getValue();
            sync();
        } catch (Exception e) {
            throw new ESGFRegistryException("Unable to properly load local Registration from [" + filename + "]",
                    e);
        }
        return this;
    }

    public synchronized boolean removeNode(String nodeHostname) {
        sync();
        if (myRegistration.getNode().remove(myNodeMap.remove(nodeHostname))) {
            dirty = true;
            touch();
        }
        return dirty;
    }

    protected void touch() {
        myRegistration.setTimeStamp((new Date()).getTime());
    }

    public Registration createRegistrationFromString(String registrationContent) {
        log.trace("Creating registration info from String:\n" + registrationContent + "\n");
        Registration fromContentRegistration = null;
        try {
            JAXBContext jc = JAXBContext.newInstance(Registration.class);
            Unmarshaller u = jc.createUnmarshaller();
            JAXBElement<Registration> root = u.unmarshal(new StreamSource(new StringReader(registrationContent)),
                    Registration.class);
            fromContentRegistration = root.getValue();
        } catch (Exception e) {
            log.error(e);
        }
        return fromContentRegistration;
    }

    //Delegate out to our helper so we can get things out of the
    //esgf_security.group database table
    private void loadAttributeServiceGroups(AttributeService attrSvc) {
        //lazy instantiate this guy... Because it is only used when
        //this service is available, which is not on every node, so
        //don't waste memory :-) -gavin
        if (helperDAO == null) {
            helperDAO = new RegistrationGleanerHelperDAO(props);
        }
        helperDAO.loadAttributeServiceGroups(attrSvc);
    }

    //Allow this class to be used as a command line tool for bootstrapping this node.
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0].equals("bootstrap")) {
                System.out.println(args[0] + "ing...");
                (new RegistrationGleaner()).createMyRegistration().saveRegistration();
            } else if (args[0].equals("load")) {
                System.out.println(args[0] + "ing...");
                //(new RegistrationGleaner()).loadMyRegistration().saveRegistration();
                if (args.length == 2) {
                    System.out.println((new RegistrationGleaner()).loadMyRegistration(args[1]));
                } else {
                    System.out.println((new RegistrationGleaner()).loadMyRegistration());
                }
            } else {
                System.out.println("illegal arg: " + args[0]);
            }
        }
    }

}