/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.brooklyn.core.entity; import java.util.Map; import java.util.Set; import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.Group; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.objs.BrooklynObject; import org.apache.brooklyn.api.objs.BrooklynObject.RelationSupport; import org.apache.brooklyn.api.policy.Policy; import org.apache.brooklyn.api.relations.RelationshipType; import org.apache.brooklyn.core.objs.BrooklynObjectInternal; import org.apache.brooklyn.core.relations.AbstractBasicRelationSupport; import org.apache.brooklyn.core.relations.RelationshipTypes; import org.apache.brooklyn.util.collections.MutableMap; import com.google.common.annotations.Beta; /** TODO these relations are not used yet; see issue where this is introduced and email thread */ @Beta public class EntityRelations<T extends BrooklynObject> { /** {@link #MANAGER_OF} indicates that one entity is the manager of another entity, * in the internal Brooklyn management hierarchy model. * Apart from root {@link Application} entities, every deployed entity must have exactly one manager. * The inverse relationship is {@link #MANAGED_BY}. */ public static final RelationshipType<Entity,Entity> MANAGER_OF = RelationshipTypes.newRelationshipPair( "manager", "managers", Entity.class, "manager_of", "managed child", "managed children", Entity.class, "managed_by"); /** Inverse of {@link #MANAGER_OF}. */ public static final RelationshipType<Entity,Entity> MANAGED_BY = MANAGER_OF.getInverseRelationshipType(); /** {@link #GROUP_CONTAINS} indicates that one entity, typically a {@link Group}, * has zero or more entities which are labelled as "members" of that group entity. * What membership means will depend on the group entity. * An entity may be a member of any number of other entities. * The inverse relationship is {@link #IN_GROUP}. */ public static final RelationshipType<Entity,Entity> GROUP_CONTAINS = RelationshipTypes.newRelationshipPair( "group", "groups", Entity.class, "group_contains", "member", "members", Entity.class, "in_group"); /** Inverse of {@link #GROUP_CONTAINS}. */ public static final RelationshipType<Entity,Entity> IN_GROUP = GROUP_CONTAINS.getInverseRelationshipType(); /** {@link #HAS_TARGET} indicates that one entity directs to one or more other entities. * What this targeting relationship means depends on the targetter. * The inverse relationship is {@link #TARGETTED_BY}. */ public static final RelationshipType<Entity,Entity> HAS_TARGET = RelationshipTypes.newRelationshipPair( "targetter", "targetters", Entity.class, "has_target", "target", "targets", Entity.class, "targetted_by"); /** Inverse of {@link #HAS_TARGET}. */ public static final RelationshipType<Entity,Entity> TARGETTED_BY = HAS_TARGET.getInverseRelationshipType(); /** {@link #ACTIVE_PARENT_OF} indicates that one entity is should be considered as the logical parent of another, * e.g. for presentation purposes to the end user. * Frequently this relationship coincides with a {@link #MANAGED_BY} relationship, * but sometimes some managed children are there for purposes the designers consider less important, * and they can choose to suppress the {@link #ACTIVE_PARENT_OF} relationship * so that the active children is a subset of the managed children. * <p> * One recommended consideration is whether the child should be shown in a default tree view. * Whilst a user can always fina a way to see all managed children, * it may be the case that only some of those are of primary interest, * and it is to identify those that this relationship exists. * <p> * It is permitted that an entity be an {@link #ACTIVE_PARENT_OF} an entity for which it is not a manager, * but in most cases a different relationship type is more appropriate where there is not also a management relationship. * <p> * The inverse relationship is {@link #ACTIVE_CHILD_OF}, * and an entity should normally be an {@link #ACTIVE_CHILD_OF} zero or one entities. */ public static final RelationshipType<Entity,Entity> ACTIVE_PARENT_OF = RelationshipTypes.newRelationshipPair( "parent", "parents", Entity.class, "parent_of_active", "active child", "active children", Entity.class, "active_child_of"); /** Inverse of {@link #ACTIVE_PARENT_OF}. */ public static final RelationshipType<Entity,Entity> ACTIVE_CHILD_OF = ACTIVE_PARENT_OF.getInverseRelationshipType(); /** {@link #HAS_POLICY} indicates that an entity has a policy associated to it. * The inverse relationship is {@link #POLICY_FOR}. */ public static final RelationshipType<Entity,Policy> HAS_POLICY = RelationshipTypes.newRelationshipPair( "entity", "entities", Entity.class, "has_policy", "policy", "policies", Policy.class, "policy_for"); /** Inverse of {@link #HAS_POLICY}. */ public static final RelationshipType<Policy,Entity> POLICY_FOR = HAS_POLICY.getInverseRelationshipType(); // ---- // TODO replace by relations stored in catalog when catalog supports arbitrary types private static Map<String,RelationshipType<? extends BrooklynObject, ? extends BrooklynObject>> KNOWN_RELATIONSHIPS = MutableMap.of(); private static void addRelationship(RelationshipType<? extends BrooklynObject, ? extends BrooklynObject> r) { KNOWN_RELATIONSHIPS.put(r.getRelationshipTypeName(), r); if (r.getInverseRelationshipType()!=null) { KNOWN_RELATIONSHIPS.put(r.getInverseRelationshipType().getRelationshipTypeName(), r.getInverseRelationshipType()); } } static { addRelationship(MANAGER_OF); addRelationship(GROUP_CONTAINS); addRelationship(HAS_TARGET); addRelationship(HAS_POLICY); } /** Find the typed Relationship instance for the given relationship name, if known; * behaviour is not guaranteed by the API if not known (hence the Beta marker), * it may fail fast or return null or create a poor-man's relationship instance. */ @Beta public static RelationshipType<? extends BrooklynObject, ? extends BrooklynObject> lookup(ManagementContext mgmt, String relationshipTypeName) { if (relationshipTypeName==null) return null; RelationshipType<? extends BrooklynObject, ? extends BrooklynObject> result = KNOWN_RELATIONSHIPS.get(relationshipTypeName); if (result!=null) return result; /* TODO ultimately we'd like to support arbitrary relationships via persistence and lookup against the catalog; * however for now, so that we can persist nicely (without catalog items for relationships) * we are smart about the relationships defined here, and we return a poor-man's version for items elsewhere. * * for now, a poor-man's relationship; if not in catalog ultimately we should fail. */ return RelationshipTypes.newRelationshipOneway("source", "sources", BrooklynObject.class, relationshipTypeName, "target", "targets", BrooklynObject.class); } /** * As {@link RelationSupport#getRelationshipTypes()} for the given object. Callers can use either method. * See {@link AbstractBasicRelationSupport} for a discussion of why double dispatch is used and both methods are present. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static <T extends BrooklynObject> Set<RelationshipType<? super T,? extends BrooklynObject>> getRelationshipTypes(T source) { return (Set) ((BrooklynObjectInternal)source).relations().getLocalBackingStore().getRelationshipTypes(); } /** * As {@link RelationSupport#getRelations(RelationshipType)} for the given object. Callers can use either method. * See {@link AbstractBasicRelationSupport} for a discussion of why double dispatch is used and both methods are present. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static <T extends BrooklynObject,U extends BrooklynObject> Set<U> getRelations(RelationshipType<? super T,U> relationship, T source) { return (Set) ((BrooklynObjectInternal)source).relations().getLocalBackingStore().getRelations((RelationshipType)relationship); } /** * As {@link RelationSupport#add(RelationshipType, BrooklynObject)} for the given object. Callers can use either method. * See {@link AbstractBasicRelationSupport} for a discussion of why double dispatch is used and both methods are present. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static <T extends BrooklynObject,U extends BrooklynObject> void add(T source, RelationshipType<? super T,? super U> relationship, U target) { ((BrooklynObjectInternal)source).relations().getLocalBackingStore().add((RelationshipType)relationship, target); if (relationship.getInverseRelationshipType()!=null) ((BrooklynObjectInternal)target).relations().getLocalBackingStore().add((RelationshipType)relationship.getInverseRelationshipType(), source); } /** * As {@link RelationSupport#remove(RelationshipType, BrooklynObject)} for the given object. Callers can use either method. * See {@link AbstractBasicRelationSupport} for a discussion of why double dispatch is used and both methods are present. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static <T extends BrooklynObject,U extends BrooklynObject> void remove(T source, RelationshipType<? super T,? super U> relationship, U target) { ((BrooklynObjectInternal)source).relations().getLocalBackingStore().remove((RelationshipType)relationship, target); if (relationship.getInverseRelationshipType()!=null) ((BrooklynObjectInternal)target).relations().getLocalBackingStore().remove((RelationshipType)relationship.getInverseRelationshipType(), source); } }