org.nimbustools.ctxbroker.service.ContextBrokerHomeImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.nimbustools.ctxbroker.service.ContextBrokerHomeImpl.java

Source

/*
 * Copyright 1999-2008 University of Chicago
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package org.nimbustools.ctxbroker.service;

import org.nimbustools.ctxbroker.Identity;
import org.nimbustools.ctxbroker.BrokerConstants;
import org.nimbustools.ctxbroker.ContextBrokerException;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Contextualization_Type;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Provides_Type;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Requires_Type;
import org.nimbustools.ctxbroker.generated.gt4_0.description.IdentityProvides_Type;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Requires_TypeIdentity;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Cloudcluster_Type;
import org.nimbustools.ctxbroker.generated.gt4_0.description.Cloudworkspace_Type;
import org.nimbustools.ctxbroker.generated.gt4_0.description.AgentDescription_Type;
import org.nimbustools.ctxbroker.security.BootstrapFactory;
import org.nimbustools.ctxbroker.security.BootstrapInformation;
import org.globus.wsrf.impl.ResourceHomeImpl;
import org.globus.wsrf.impl.SimpleResourceKey;
import org.globus.wsrf.ResourceKey;
import org.globus.wsrf.ResourceException;
import org.globus.wsrf.Constants;
import org.globus.wsrf.config.ConfigException;
import org.globus.wsrf.utils.AddressingUtils;
import org.globus.wsrf.container.ServiceHost;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.axis.components.uuid.UUIDGen;
import org.apache.axis.components.uuid.UUIDGenFactory;
import org.apache.axis.message.addressing.EndpointReferenceType;
import org.apache.axis.message.MessageElement;

import javax.naming.InitialContext;
import java.io.IOException;
import java.util.Calendar;
import java.util.Hashtable;

public class ContextBrokerHomeImpl extends ResourceHomeImpl implements ContextBrokerHome {

    // -------------------------------------------------------------------------
    // STATIC VARIABLES
    // -------------------------------------------------------------------------

    private static final Log logger = LogFactory.getLog(ContextBrokerHomeImpl.class.getName());

    public static final String CONTEXTUALIZATION_BOOTSTRAP_FACTORY = Constants.JNDI_SERVICES_BASE_NAME
            + BrokerConstants.CTX_BROKER_PATH + "/ctxBrokerBootstrapFactory";

    // -------------------------------------------------------------------------
    // INSTANCE VARIABLES
    // -------------------------------------------------------------------------

    private final String serviceAddress;

    private boolean initialized;

    private UUIDGen uuidGen;

    private BootstrapFactory bootstrapFactory;

    // String hostname --> Integer workspID
    private final Hashtable<String, Integer> hostnameMap = new Hashtable<String, Integer>();

    // String ip-address --> Integer workspID
    private final Hashtable<String, Integer> ipMap = new Hashtable<String, Integer>();

    // the lowest unused integer ID
    private int nextID = 1;

    // -------------------------------------------------------------------------
    // CONSTRUCTOR
    // -------------------------------------------------------------------------

    public ContextBrokerHomeImpl() throws IOException {
        super();
        this.keyTypeName = BrokerConstants.CONTEXTUALIZATION_RESOURCE_KEY_QNAME;
        this.serviceAddress = ServiceHost.getBaseURL() + BrokerConstants.CTX_BROKER_PATH;
    }

    // -------------------------------------------------------------------------
    // SETUP
    // -------------------------------------------------------------------------

    public synchronized void initialize() throws Exception {

        super.initialize();

        if (this.initialized) {
            logger.warn("already initialized: Nimbus Context Broker");
            return;
        }

        logger.info("Setting up Nimbus Context Broker");

        this.uuidGen = UUIDGenFactory.getUUIDGen();

        this.bootstrapFactory = discoverBootstrapFactory();

        this.initialized = true;

        logger.info("Ready: Nimbus Context Broker");
    }

    public static BootstrapFactory discoverBootstrapFactory() throws Exception {

        InitialContext ctx = null;
        try {
            ctx = new InitialContext();

            final BootstrapFactory factory = (BootstrapFactory) ctx.lookup(CONTEXTUALIZATION_BOOTSTRAP_FACTORY);

            if (factory == null) {
                throw new Exception("null from JNDI for BootstrapFactory (?)");
            }

            return factory;

        } finally {
            if (ctx != null) {
                ctx.close();
            }
        }
    }

    // -------------------------------------------------------------------------
    // implements ContextBrokerHome
    // -------------------------------------------------------------------------

    public String getBrokerURL() throws ContextBrokerException {
        return this.serviceAddress;
    }

    public String getContextSecret(EndpointReferenceType ref) throws ContextBrokerException {
        final ResourceKey ctxKey = this.getResourceKey(ref);
        final ContextBrokerResource resource = this.findResourceNoSecurity(ctxKey);
        BootstrapInformation bootstrap = resource.getBootstrap();

        // current secret string is a separated list:
        /*
           Separator
           Position 0: Public certificate (pem) to contact ctx service
           Separator
           Position 1: Private key (pem) to contact ctx service
           Separator
        */

        StringBuffer buf = new StringBuffer();
        buf.append(ContextBrokerHome.FIELD_SEPARATOR).append(bootstrap.getPublicX509String())
                .append(ContextBrokerHome.FIELD_SEPARATOR).append(bootstrap.getPrivateString())
                .append(ContextBrokerHome.FIELD_SEPARATOR);
        return buf.toString();
    }

    /**
     * @see ContextBrokerHome#addWorkspace
     */
    public void addWorkspace(ContextBrokerResource resource, Integer workspaceID, Identity[] identities,
            AgentDescription_Type ctxDocAndID)

            throws ContextBrokerException {

        if (identities == null) {
            throw new IllegalArgumentException("identities may not be null");
        }

        if (ctxDocAndID == null) {
            final String err = "A previously unknown workspace's context " + "agent is invoking "
                    + "an operation before retrieve, we cannot register it.\n" + "Identity dump: "
                    + this.identitiesDump(identities);
            throw new ContextBrokerException(err);
        }

        if (workspaceID == null) {
            throw new IllegalArgumentException("workspaceID may not be null");
        }

        if (identities.length > 2 || identities.length == 0) {
            throw new ContextBrokerException(
                    "these arbitrary identities are not supported at the moment," + "you may only use 1 or 2 NICs");
        }

        Cloudcluster_Type cluster = ctxDocAndID.getCluster();

        // checks that there is nothing illegal and that one and only
        // one section will be marked active
        int totalNodes = this.clusterSanityAndCount(cluster);
        logger.debug("total nodes: " + totalNodes);

        final Cloudworkspace_Type[] vms = cluster.getWorkspace();
        Contextualization_Type ctxDoc = null;
        for (Cloudworkspace_Type vm : vms) {
            if (Boolean.TRUE.equals(vm.getActive())) {
                ctxDoc = vm.getCtx(); // clusterSanityAndCount guarantees one and only one
                break;
            }
        }

        // clusterSanity checked this already, this is to satisfy automatic
        // code analysis
        if (ctxDoc == null) {
            throw new NullPointerException();
        }

        this.basicCtxdocValidate(ctxDoc);

        for (Identity identity : identities) {
            final String hostname = identity.getHostname();
            if (hostname != null) {
                logger.debug("hostname: " + hostname);
                this.newHostname(workspaceID, hostname, resource);
            }
            final String ip = identity.getIp();
            if (ip != null) {
                logger.debug("ip: " + ip);
                this.newIP(workspaceID, ip, resource);
            }
        }

        resource.addWorkspace(workspaceID, identities, ctxDoc.getRequires(), ctxDoc.getProvides(), totalNodes);
    }

    /**
     * @see ContextBrokerHome#getResourceKey
     */
    public ResourceKey getResourceKey(EndpointReferenceType epr) throws ContextBrokerException {

        return new SimpleResourceKey(this.getKeyTypeName(), this.getID(epr));
    }

    /**
     * @see ContextBrokerHome#createNewResource
     */
    public EndpointReferenceType createNewResource(String callerDN, boolean allowInjections)

            throws ContextBrokerException {

        if (!this.initialized) {
            throw new ContextBrokerException(ContextBrokerHomeImpl.class.getName() + " not initialized.");
        }

        final ContextBrokerResource resource;
        try {
            resource = (ContextBrokerResource) createNewInstance();
        } catch (Exception e) {
            throw new ContextBrokerException("", e);
        }

        final String uuid = this.uuidGen.nextUUID();
        resource.setID(uuid);

        resource.setAllowInjections(allowInjections);

        // TODO: make this limit configurable and/or possibly make a dynamic
        //       decision.  For example, if the scheduler is NOT best effort
        //       then it is probably OK to just make this expire in a day or
        //       even an hour.  Also, if re-contextualization is implemented,
        //       presumbaly the VMs (for now) would contact with same cred
        //       so expiration will affect things in that situation
        final Calendar expires = Calendar.getInstance();
        expires.add(Calendar.MONTH, 1);

        final BootstrapInformation bootstrap = this.bootstrapFactory.newBootstrap(uuid, this.serviceAddress,
                expires);

        resource.setBootstrap(bootstrap);

        // in the future, policy should come from elsewhere
        try {
            resource.initSecureResource(callerDN, bootstrap.getBootstrapDN());
        } catch (ConfigException e) {
            throw new ContextBrokerException("", e);
        }

        logger.info(
                "WS-CTX created new contextualization " + "resource: '" + uuid + "' for DN = '" + callerDN + "'");

        ResourceKey key = this.getResourceKey(uuid);
        this.add(key, resource);
        return getEPR(key);
    }

    // -------------------------------------------------------------------------
    // OTHER/IMPL
    // -------------------------------------------------------------------------

    private void newHostname(Integer vmid, String hostname, ContextBrokerResource resource) {

        if (vmid == null) {
            throw new IllegalArgumentException("vmid may not be null");
        }

        if (hostname == null) {
            throw new IllegalArgumentException("hostname may not be null");
        }

        final String rsrc = (String) resource.getID();
        if (rsrc == null) {
            throw new IllegalArgumentException("resource ID may not be null");
        }

        this.noPersistenceHostnameAddition(rsrc, vmid, hostname);
    }

    private void newIP(Integer vmid, String ip, ContextBrokerResource resource) {

        if (vmid == null) {
            throw new IllegalArgumentException("vmid may not be null");
        }

        if (ip == null) {
            throw new IllegalArgumentException("ip may not be null");
        }

        final String rsrc = (String) resource.getID();
        if (rsrc == null) {
            throw new IllegalArgumentException("resource ID may not be null");
        }

        this.noPersistenceIPAddition(rsrc, vmid, ip);
    }

    public synchronized Integer getID(ContextBrokerResource resource, IdentityProvides_Type[] identities,
            AgentDescription_Type sent) throws ContextBrokerException {

        if (resource == null) {
            throw new IllegalArgumentException("resource may not be null");
        }

        if (identities == null || identities.length == 0) {
            throw new IllegalArgumentException("no identites");
        }

        final String rsrc = (String) resource.getID();
        if (rsrc == null) {
            throw new IllegalArgumentException("resource ID may not be null");
        }

        for (IdentityProvides_Type identity : identities) {
            if (identity == null) {
                logger.error("Null identity in request, ID dump: " + this.identitiesDump(identities));
                return null;
            }
            String ip = identity.getIp();
            if (ip != null) {
                final Integer result = noPersistenceIPQuery(rsrc, ip);
                if (result != null) {
                    return result;
                }
            } else {
                logger.error("Null IP in request, ID dump: " + this.identitiesDump(identities));
                return null;
            }
        }

        for (IdentityProvides_Type identity : identities) {
            String hostname = identity.getHostname();
            if (hostname != null) {
                final Integer result = noPersistenceHostnameQuery(rsrc, hostname);
                if (result != null) {
                    return result;
                }
            } else {
                logger.error("Null hostname in request, ID dump: " + this.identitiesDump(identities));
                return null;
            }
        }

        for (IdentityProvides_Type identity : identities) {
            String iface = identity.get_interface();
            if (iface == null) {
                logger.error("Null interface in request, ID dump: " + this.identitiesDump(identities));
                return null;
            }
        }

        // create an integer ID for this member of the context that we have not
        // seen before

        synchronized (this) {

            // nothing is null or missing except possibly pubkey

            final StringBuffer buf = new StringBuffer();
            buf.append("Broker hearing from a VM for first time, IP(s)=");

            Identity[] ids = new Identity[identities.length];
            for (int i = 0; i < identities.length; i++) {
                ids[i] = new Identity();
                ids[i].setIface(identities[i].get_interface());
                ids[i].setIp(identities[i].getIp());
                buf.append(" '").append(identities[i].getIp()).append("'");
                ids[i].setHostname(identities[i].getHostname());
                ids[i].setPubkey(identities[i].getPubkey());
            }

            final Integer thisID = this.nextID;
            this.nextID = this.nextID + 1;

            buf.append(" and internal ID # = ").append(thisID.toString());
            logger.info(buf.toString());

            this.addWorkspace(resource, thisID, ids, sent);
        }

        return null;
    }

    // for errors
    private String identitiesDump(IdentityProvides_Type[] identities) {
        final StringBuffer buf = new StringBuffer();
        for (int i = 0; i < identities.length; i++) {
            buf.append("IdentityProvides_Type #").append(i);
            IdentityProvides_Type id = identities[i];
            if (id == null) {
                buf.append(" is null.\n");
            } else {
                buf.append(":\n").append(" - interface: '").append(id.get_interface()).append("'\n");
                buf.append(" - ip: '").append(id.getIp()).append("'\n");
                buf.append(" - hostname: '").append(id.getHostname()).append("'\n");
                buf.append(" - ssh pubkey: '").append(id.getPubkey()).append("'\n");
            }
        }
        return buf.toString();
    }

    private String identitiesDump(Identity[] identities) {
        final StringBuffer buf = new StringBuffer();
        for (int i = 0; i < identities.length; i++) {
            buf.append("IdentityProvides_Type #").append(i);
            Identity id = identities[i];
            if (id == null) {
                buf.append(" is null.\n");
            } else {
                buf.append(":\n").append(" - interface: '").append(id.getIface()).append("'\n");
                buf.append(" - ip: '").append(id.getIp()).append("'\n");
                buf.append(" - hostname: '").append(id.getHostname()).append("'\n");
                buf.append(" - ssh pubkey: '").append(id.getPubkey()).append("'\n");
            }
        }
        return buf.toString();
    }

    private Integer noPersistenceIPQuery(String rsrc, String ip) {
        return this.ipMap.get(rsrc + ip);
    }

    private void noPersistenceIPAddition(String rsrc, Integer id, String ip) {
        this.ipMap.put(rsrc + ip, id);
    }

    private Integer noPersistenceHostnameQuery(String rsrc, String hostname) {
        return this.hostnameMap.get(rsrc + hostname);
    }

    private void noPersistenceHostnameAddition(String rsrc, Integer id, String hostname) {
        this.hostnameMap.put(rsrc + hostname, id);
    }

    // -------------------------------------------------------------------------
    // WS stuff
    // -------------------------------------------------------------------------

    private ContextBrokerResource findResourceNoSecurity(ResourceKey ctxKey) throws ContextBrokerException {

        try {
            return (ContextBrokerResource) this.find(ctxKey);
        } catch (ResourceException e) {
            String msg = "Cannot find contextualization resource: '" + getID(ctxKey) + "'";
            throw new ContextBrokerException(msg, e);
        }
    }

    private ContextBrokerResource findResource(ResourceKey ctxKey) throws ContextBrokerException {

        ContextBrokerResource resource;
        try {
            resource = (ContextBrokerResource) this.find(ctxKey);
        } catch (ResourceException e) {
            String msg = "Cannot find contextualization resource: '" + getID(ctxKey) + "'";
            throw new ContextBrokerException(msg, e);
        }

        return resource;
    }

    private int clusterSanityAndCount(Cloudcluster_Type cluster) throws ContextBrokerException {

        if (cluster == null) {
            throw new ContextBrokerException("Invalid cluster description " + "section, <cluster> is missing.");
        }

        Cloudworkspace_Type[] vms = cluster.getWorkspace();
        if (vms == null || vms.length == 0) {
            throw new ContextBrokerException(
                    "Invalid cluster description " + "section, missing workspace elements.");
        }

        int totalNodeCount = 0;

        int numActive = 0;
        for (Cloudworkspace_Type vm : vms) {

            if (vm == null) {
                throw new ContextBrokerException(
                        "Invalid cluster description section, missing a " + "workspace element in array.");
            }

            if (vm.getCtx() == null) {
                throw new ContextBrokerException(
                        "Invalid cluster description section, missing a " + "ctx element in the workspace list.");
            }

            if (vm.getQuantity() <= 0) {
                throw new ContextBrokerException("Invalid cluster description section, missing " + "quantity.");
            }

            totalNodeCount += vm.getQuantity();

            // active can be null which equals False
            final Boolean active = vm.getActive();
            if (Boolean.TRUE.equals(active)) {
                numActive += 1;
            }
        }

        if (numActive < 1) {
            throw new ContextBrokerException(
                    "Invalid cluster description, there is no " + "section marked with <active>");
        }

        if (numActive > 1) {
            throw new ContextBrokerException("Invalid cluster description, more than one "
                    + "section is marked with <active> (there are " + numActive + " marked active)");
        }

        return totalNodeCount;
    }

    private void basicCtxdocValidate(Contextualization_Type ctx) throws ContextBrokerException {

        if (ctx == null) {
            throw new IllegalArgumentException("contextualization " + "document given to validate is null");
        }

        Provides_Type provides = ctx.getProvides();
        Requires_Type requires = ctx.getRequires();

        if (provides == null && requires == null) {
            throw new ContextBrokerException(
                    "Both provides and " + "requires are missing. Will not contextualize this. "
                            + "If there is nothing to do, do not include " + "contextualization document.");
        }

        if (provides != null) {
            IdentityProvides_Type[] givenIDs = provides.getIdentity();
            if (givenIDs == null || givenIDs.length == 0) {
                throw new ContextBrokerException("Provides section is "
                        + "present but has no identity elements.  Will not " + "contextualize.");
            }
        }

        if (requires == null) {
            return;
        }

        Requires_TypeIdentity[] givenID = requires.getIdentity();

        if (givenID == null || givenID.length == 0) {
            return;
        }

        // next two exceptions are for forwards compatibility where it
        // may be possible to specify specific identities required
        // (without going through role finding which will always place
        // identities in the filled requires document for a role,
        // regardless if all identities are required or not).

        if (givenID.length > 1) {
            throw new ContextBrokerException(
                    "Given requires " + "section has multiple identity elements? Currently "
                            + "only supporting zero or one empty identity element "
                            + "in requires section (which signals all identities "
                            + "are desired).  Will not contextualize.");
        }

        if (givenID[0].getHostname() != null || givenID[0].getIp() != null || givenID[0].getPubkey() != null) {

            throw new ContextBrokerException("Given requires " + "section has an identity element with information "
                    + "in it? Currently only supporting zero or one "
                    + "*empty* identity element in requires section "
                    + "(which signals all identities are desired).  Will " + "not contextualize.");
        }
    }

    public ResourceKey getResourceKey(String id) {
        return new SimpleResourceKey(this.getKeyTypeName(), id);
    }

    public EndpointReferenceType getEPR(ResourceKey key) throws ContextBrokerException {

        final EndpointReferenceType epr;
        try {
            epr = AddressingUtils.createEndpointReference(this.serviceAddress, key);
        } catch (Exception exp) {
            throw new ContextBrokerException("Error creating " + "contextualization endpoint reference", exp);
        }
        return epr;
    }

    public String getID(EndpointReferenceType epr) throws ContextBrokerException {

        if (epr == null) {
            throw new ContextBrokerException("epr is null");
        }

        if (epr.getProperties() == null) {
            throw new ContextBrokerException("epr properties are null");
        }

        MessageElement key = epr.getProperties().get(this.getKeyTypeName());
        if (key == null) {
            throw new ContextBrokerException("contextualization " + "resource key not present in EPR");
        }

        return key.getValue();
    }

    public static String getID(ResourceKey key) throws ContextBrokerException {
        if (key == null) {
            throw new ContextBrokerException("key is null");
        }
        return (String) key.getValue();
    }
}