/* * 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.globus.wsrf.impl.security.descriptor.ResourceSecurityDescriptor; import org.globus.wsrf.impl.security.descriptor.ResourceSecurityConfig; import org.globus.wsrf.impl.SimpleResourceProperty; import org.globus.wsrf.impl.SimpleResourcePropertySet; import org.globus.wsrf.impl.ReflectionResourceProperty; import org.globus.wsrf.config.ConfigException; import org.globus.wsrf.ResourcePropertySet; import org.globus.wsrf.ResourceProperty; import org.nimbustools.ctxbroker.security.BootstrapInformation; import org.nimbustools.ctxbroker.Identity; import org.nimbustools.ctxbroker.BrokerConstants; import org.nimbustools.ctxbroker.ContextBrokerException; import org.nimbustools.ctxbroker.generated.gt4_0.types.Node_Type; import org.nimbustools.ctxbroker.generated.gt4_0.types.ContextualizationContext; import org.nimbustools.ctxbroker.generated.gt4_0.description.*; import org.nimbustools.ctxbroker.blackboard.*; import org.globus.security.gridmap.GridMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.Calendar; import java.util.List; import java.util.ArrayList; public class ContextBrokerResourceImpl implements ContextBrokerResource { // ------------------------------------------------------------------------- // STATIC VARIABLES // ------------------------------------------------------------------------- private static final Log logger = LogFactory.getLog(ContextBrokerResourceImpl.class.getName()); private static final Node_Type[] NO_NODES_RESPONSE = new Node_Type[0]; // ------------------------------------------------------------------------- // INSTANCE VARIABLES // ------------------------------------------------------------------------- private String resourceID; private BootstrapInformation bootstrap; private Blackboard blackboard; private boolean allowInjections; private boolean noMoreInjections; private final Object statusLock = new Object(); // stays null private Calendar terminationTime; // currently, mgmt policy can be only one DN, the factory-create() caller private String creatorDN; private ResourceSecurityDescriptor securityDescriptor; private String bootstrapDN; // resource properties private ResourcePropertySet propertySet; // ------------------------------------------------------------------------- // CONSTRUCTOR // ------------------------------------------------------------------------- public ContextBrokerResourceImpl() throws Exception { this.propertySet = new SimpleResourcePropertySet( BrokerConstants.CONTEXTUALIZATION_RP_SET); try { ResourceProperty rp = new SimpleResourceProperty( BrokerConstants.RP_CONTEXTUALIZATION_CONTEXT); rp.add(null); this.propertySet.add(rp); rp = new ReflectionResourceProperty( BrokerConstants.RP_CONTEXTUALIZATION_CONTEXT, this); this.propertySet.add(rp); } catch(Exception e) { logger.fatal("",e); } } // ------------------------------------------------------------------------- // GENERAL INFORMATION // ------------------------------------------------------------------------- // ContextBrokerResource interface public void setID(String id) { this.resourceID = id; } /** * Matches {@link org.globus.wsrf.ResourceKey#getValue ResoureKey.getValue()} * * ResourceIdentifier interface * * @return group id */ public Object getID() { return this.resourceID; } /** * WorkspaceContextualizationResource interface * * @return BootstrapInformation, never null */ public BootstrapInformation getBootstrap() { return this.bootstrap; } public void setBootstrap(BootstrapInformation bootstrap) { this.bootstrap = bootstrap; } // ------------------------------------------------------------------------- // BLACKBOARD // ------------------------------------------------------------------------- // always access it this way internally in the class or there may // be a check then act on the cache set public synchronized Blackboard getBlackboard() throws ContextBrokerException { if (this.resourceID == null) { throw new ContextBrokerException("no resource id yet (?)"); } if (this.blackboard == null) { this.blackboard = Blackboard.createOrGetBlackboard(this.resourceID); } return this.blackboard; } // ------------------------------------------------------------------------- // INJECT // ------------------------------------------------------------------------- public void injectData(String name, String value) throws ContextBrokerException { synchronized (this.statusLock) { this.getBlackboard().injectData(name, value); } } // called at create time only public void setAllowInjections(boolean allowInjections) { this.allowInjections = allowInjections; if (!allowInjections) { this.noMoreInjections = true; } } public void noMoreInjections() throws ContextBrokerException { if (!this.allowInjections) { throw new ContextBrokerException("Erroneous: noMoreInjections " + "called but injections weren't expected in the first" + "place"); } synchronized (this.statusLock) { if (this.noMoreInjections) { logger.warn("noMoreInjections called but this was already " + "noMoreInjections: '" + this.resourceID + "'"); } else { this.noMoreInjections = true; } } } public boolean isNoMoreInjections() { synchronized (this.statusLock) { return this.noMoreInjections; } } // ------------------------------------------------------------------------- // EXITING MESSAGES // ------------------------------------------------------------------------- public void okExit(Integer workspaceID) throws ContextBrokerException { synchronized (this.statusLock) { this.getBlackboard().okExit(workspaceID); } } public void errorExit(Integer workspaceID, short exitCode, String errorMessage) throws ContextBrokerException { synchronized (this.statusLock) { this.getBlackboard().errorExit(workspaceID, exitCode, errorMessage); } } // ------------------------------------------------------------------------- // IDENTITIES QUERY // ------------------------------------------------------------------------- public Node_Type[] identityQueryAll() throws ContextBrokerException { synchronized (this.statusLock) { final List<NodeStatus> nodes = this.getBlackboard().identities(true, null, null); return getNodeResponse(nodes); } } public Node_Type[] identityQueryHost(String host) throws ContextBrokerException { synchronized (this.statusLock) { final List<NodeStatus> nodes = this.getBlackboard().identities(false, host, null); return getNodeResponse(nodes); } } public Node_Type[] identityQueryIP(String ip) throws ContextBrokerException { synchronized (this.statusLock) { final List<NodeStatus> nodes = this.getBlackboard().identities(false, null, ip); return getNodeResponse(nodes); } } private static Node_Type[] getNodeResponse(List<NodeStatus> nodeList) { if (nodeList.isEmpty()) { return NO_NODES_RESPONSE; } final List<Node_Type> resultList = new ArrayList<Node_Type>(nodeList.size()); for (final NodeStatus node : nodeList) { if (node != null) { resultList.add(getOneNodeResponse(node)); } } return resultList.toArray( new Node_Type[resultList.size()]); } private static Node_Type getOneNodeResponse(NodeStatus node) { final Node_Type xmlNode = new Node_Type(); /* identities */ final List<IdentityProvides_Type> xmlIdentsList = new ArrayList<IdentityProvides_Type>(3); for (Identity ident : node.getIdentities()) { if (ident != null) { xmlIdentsList.add(idToXML(ident)); } } final IdentityProvides_Type[] xmlIdents = xmlIdentsList.toArray( new IdentityProvides_Type[xmlIdentsList.size()]); xmlNode.setIdentity(xmlIdents); /* status */ if (node.isOkOccurred()) { xmlNode.setExited(true); xmlNode.setOk(true); } else if (node.isErrorOccurred()) { xmlNode.setExited(true); xmlNode.setErrorCode(node.getErrorCode()); xmlNode.setErrorMessage(node.getErrorMessage()); } else { xmlNode.setExited(false); } return xmlNode; } private static IdentityProvides_Type idToXML(Identity ident) { final IdentityProvides_Type xml = new IdentityProvides_Type(); xml.set_interface(ident.getIface()); xml.setHostname(ident.getHostname()); xml.setIp(ident.getIp()); xml.setPubkey(ident.getPubkey()); return xml; } // ------------------------------------------------------------------------- // RETRIEVE // ------------------------------------------------------------------------- // Only returning something if locked and complete. In the future we can // mess with "chunks" being sent asynchronously in order to avoid provider // and requires deadlocks and to speed up VM configuration // (though increasing perhaps unecessarily the message sizes and XML // parsing/creation times). public Requires_Type retrieve(Integer workspaceID) throws ContextBrokerException { synchronized (this.statusLock) { if (this.noMoreInjections) { final NodeManifest nodeManifest = this.getBlackboard().retrieve(workspaceID); if (nodeManifest == null) { return null; } return translateNodeManifest(nodeManifest); } return null; } } private Requires_Type translateNodeManifest(NodeManifest manifest) { Requires_Type requires = new Requires_Type(); final Requires_TypeRole[] roles = new Requires_TypeRole[manifest.getRequiredRoles().size()]; for (int i = 0; i < roles.length; i++) { final RoleIdentityPair roleIdentityPair = manifest.getRequiredRoles().get(i); final Requires_TypeRole role = new Requires_TypeRole(); role.setName(roleIdentityPair.getRole()); role.set_value(roleIdentityPair.getIdentity().getIp()); roles[i] = role; } requires.setRole(roles); final Requires_TypeData[] datas = new Requires_TypeData[manifest.getData().size()]; for (int i = 0; i < datas.length; i++) { final DataPair dataPair = manifest.getData().get(i); final Requires_TypeData data = new Requires_TypeData(); data.setName(dataPair.getName()); data.set_value(dataPair.getValue()); datas[i] = data; } requires.setData(datas); final Requires_TypeIdentity[] ids = new Requires_TypeIdentity[manifest.getIdentities().size()]; for (int i = 0; i < ids.length; i++) { final Identity identity = manifest.getIdentities().get(i); ids[i] = new Requires_TypeIdentity( identity.getHostname(), identity.getIp(), identity.getPubkey()); } requires.setIdentity(ids); return requires; } // ------------------------------------------------------------------------- // ADD WORKSPACE // ------------------------------------------------------------------------- public void addWorkspace(Integer workspaceID, Identity[] identities, Requires_Type requires, Provides_Type provides, int totalNodes) throws ContextBrokerException { if (provides == null && requires == null) { throw new IllegalArgumentException("Both provides and requires " + "are null? Don't add this workspace to the " + "contextualization resource. workspaceID #" + workspaceID); } boolean allIdentitiesRequired = false; if(requires != null){ final Requires_TypeIdentity[] givenID = requires.getIdentity(); if(givenID != null && givenID.length > 0){ // 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 #" + workspaceID + "."); } 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 #" + workspaceID + "."); } allIdentitiesRequired = true; } } if(!allIdentitiesRequired){ logger.trace("#" + workspaceID + " does not require all " + "identities, no identity element in given requires " + "section"); } this.getBlackboard().addWorkspace( workspaceID, identities, allIdentitiesRequired, getRequiredRoles(workspaceID, requires), getDataPairs(requires), getProvidedRoleDescriptions(workspaceID, provides), totalNodes); } private ProvidedRoleDescription[] getProvidedRoleDescriptions(Integer workspaceID, Provides_Type provides) { if(provides != null){ Provides_TypeRole[] roles = provides.getRole(); if (roles != null && roles.length > 0){ ProvidedRoleDescription[] roleDescs = new ProvidedRoleDescription[roles.length]; for (int i = 0; i < roles.length; i++) { Provides_TypeRole role = roles[i]; roleDescs[i] = new ProvidedRoleDescription(role.get_value(), role.get_interface()); } return roleDescs; } } //If there are no provided roles specified if (logger.isTraceEnabled()) { logger.trace("Provides section for #" + workspaceID + " has " + "identities but has no role-provides elements. " + "Allowing, perhaps this is only to get identity " + "into contextualization context's all-identity " + "list."); } return null; } private RequiredRole[] getRequiredRoles(Integer workspaceID, Requires_Type requires) throws ContextBrokerException { if(requires != null){ final RequiredRole[] roles; final Requires_TypeRole[] requiredRoles = requires.getRole(); if (requiredRoles != null && requiredRoles.length > 0) { roles = new RequiredRole[requiredRoles.length]; for (int i = 0; i < requiredRoles.length; i++) { Requires_TypeRole requiredRole = requiredRoles[i]; roles[i] = getRequiredRole(requiredRole); } return roles; } } //If there are no required roles specified if (logger.isTraceEnabled()) { logger.trace("Requires section for #" + workspaceID + " has " + "no role-required elements." + " Allowing, perhaps the only thing required by " + "this node is the contextualization context's " + "all-identity list and/or just data elements."); } return null; } private DataPair[] getDataPairs(Requires_Type requires) throws ContextBrokerException { if(requires != null){ final DataPair[] dataPairs; final Requires_TypeData[] datas = requires.getData(); if (datas != null) { dataPairs = new DataPair[datas.length]; for (int i = 0; i < datas.length; i++) { Requires_TypeData data = datas[i]; final String dataName = data.getName(); if (dataName == null || dataName.trim().length() == 0) { // does not happen when object is created via XML (which is usual) throw new ContextBrokerException("Empty data element name (?)"); } dataPairs[i] = new DataPair(dataName, data.get_value()); } return dataPairs; } } return null; } private RequiredRole getRequiredRole(Requires_TypeRole typeRole) throws ContextBrokerException { // SAMPLE // <requires> // <identity /> // <role name="torqueserver" hostname="true" pubkey="true" /> // <role name="nfsserver" /> // </requires> // name attribute is relevant for given requires roles, NOT value final String roleName = typeRole.getName(); if (roleName == null || roleName.trim().equals("")) { // does not happen when object is created via XML (which is usual) throw new ContextBrokerException("Empty role name (?)"); } boolean hostRequired = false; if (typeRole.getHostname() != null && typeRole.getHostname()) { hostRequired = true; } boolean keyRequired = false; if (typeRole.getPubkey() != null && typeRole.getPubkey()) { keyRequired = true; } return new RequiredRole(roleName, hostRequired, keyRequired); } // ------------------------------------------------------------------------- // SECURE RESOURCE // ------------------------------------------------------------------------- // SecureResource interface public ResourceSecurityDescriptor getSecurityDescriptor() { return this.securityDescriptor; } // ContextBrokerResource interface public String getCreatorDN() { return this.creatorDN; } // ContextBrokerResource interface public String getBootstrapDN() { return this.bootstrapDN; } /** * Policy is the factory-create() caller that caused the resource to * be created as well as the bootstrap credential. * * Used for creating the resource object in the first place or bringing * out of persistence, no alteration of this policy is supported yet. * * WorkspaceContextualizationResource interface * * @param creatorDN factory-create() caller * @param bootstrapDN DN of bootstrap credential * @throws ConfigException if problem */ public void initSecureResource(String creatorDN, String bootstrapDN) throws ConfigException { this.creatorDN = creatorDN; this.bootstrapDN = bootstrapDN; if (this.securityDescriptor == null) { final ResourceSecurityConfig securityConfig = new ResourceSecurityConfig( BrokerConstants.SERVICE_SECURITY_CONFIG); securityConfig.init(); this.securityDescriptor = securityConfig.getSecurityDescriptor(); } this.securityDescriptor.setInitialized(false); final GridMap map = new GridMap(); if (this.creatorDN != null) { map.map(this.creatorDN, "fakeuserid"); } if (this.bootstrapDN != null) { map.map(this.bootstrapDN, "fakeuserid"); } this.securityDescriptor.setGridMap(map); } // ------------------------------------------------------------------------- // implements ResourceProperties // ------------------------------------------------------------------------- public ResourcePropertySet getResourcePropertySet() { return this.propertySet; } /* For ReflectionResourceProperty RP_CONTEXTUALIZATION_CONTEXT */ public ContextualizationContext getContextualizationContext() { final CtxStatus status; try { status = this.getBlackboard().getStatus(); } catch (ContextBrokerException e) { logger.error("Problem: returning null contextualization context " + "RP for '" + this.resourceID + "': " + e.getMessage()); return null; } final ContextualizationContext context = new ContextualizationContext(); context.setNoMoreInjections(noMoreInjections); context.setAllOK(status.isAllOk()); context.setErrorPresent(status.isErrorOccurred()); context.setComplete(status.isComplete()); return context; } // ------------------------------------------------------------------------- // implements ResourceLifetime // ------------------------------------------------------------------------- public Calendar getCurrentTime() { return Calendar.getInstance(); } public Calendar getTerminationTime() { return this.terminationTime; } public void setTerminationTime(Calendar time) { this.terminationTime = time; } }