/* * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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.hawkular.inventory.base; import static org.hawkular.inventory.api.Action.created; import static org.hawkular.inventory.api.Relationships.Direction.outgoing; import static org.hawkular.inventory.api.Relationships.WellKnown.contains; import static org.hawkular.inventory.api.Relationships.WellKnown.defines; import static org.hawkular.inventory.api.Relationships.WellKnown.incorporates; import static org.hawkular.inventory.api.Relationships.WellKnown.isParentOf; import static org.hawkular.inventory.api.filters.With.id; import java.util.ArrayList; import java.util.List; import org.hawkular.inventory.api.Data; import org.hawkular.inventory.api.EntityAlreadyExistsException; import org.hawkular.inventory.api.EntityNotFoundException; import org.hawkular.inventory.api.Metrics; import org.hawkular.inventory.api.RelationAlreadyExistsException; import org.hawkular.inventory.api.RelationNotFoundException; import org.hawkular.inventory.api.Resources; import org.hawkular.inventory.api.filters.Filter; import org.hawkular.inventory.api.filters.RecurseFilter; import org.hawkular.inventory.api.filters.Related; import org.hawkular.inventory.api.filters.With; import org.hawkular.inventory.api.model.DataEntity; import org.hawkular.inventory.api.model.Metric; import org.hawkular.inventory.api.model.Relationship; import org.hawkular.inventory.api.model.Resource; import org.hawkular.inventory.api.model.ResourceType; import org.hawkular.inventory.base.spi.Discriminator; import org.hawkular.inventory.base.spi.ElementNotFoundException; import org.hawkular.inventory.paths.CanonicalPath; import org.hawkular.inventory.paths.DataRole; import org.hawkular.inventory.paths.Path; import org.hawkular.inventory.paths.SegmentType; /** * @author Lukas Krejci * @since 0.1.0 */ public final class BaseResources { private BaseResources() { } public static class ReadWrite<BE> extends Mutator<BE, Resource, Resource.Blueprint, Resource.Update, String> implements Resources.ReadWrite { public ReadWrite(TraversalContext<BE, Resource> context) { super(context); } @Override protected String getProposedId(Transaction<BE> tx, Resource.Blueprint blueprint) { return blueprint.getId(); } @Override protected EntityAndPendingNotifications<BE, Resource> wireUpNewEntity(Discriminator discriminator, BE entity, Resource.Blueprint blueprint, CanonicalPath parentPath, BE parent, Transaction<BE> tx) { BE resourceTypeObject; CanonicalPath resourceTypePath = null; try { CanonicalPath tenant = CanonicalPath.of().tenant(parentPath.ids().getTenantId()).get(); resourceTypePath = Util.canonicalize(blueprint.getResourceTypePath(), tenant, parentPath, ResourceType.SEGMENT_TYPE); resourceTypeObject = tx.find(discriminator, resourceTypePath); } catch (ElementNotFoundException e) { throw new IllegalArgumentException("Resource type '" + blueprint.getResourceTypePath() + "' not found" + " when resolved to '" + resourceTypePath + "' while trying to wire up a new resource on path '" + parentPath.extend(SegmentType.r, blueprint.getId()).get() + "'."); } //specifically do NOT check relationship rules, here because defines cannot be created "manually". //here we "know what we are doing" and need to create the defines relationship to capture the //contract of the resource. BE r = tx.relate(discriminator, resourceTypeObject, entity, defines.name(), null); CanonicalPath entityPath = tx.extractCanonicalPath(entity); resourceTypePath = tx.extractCanonicalPath(resourceTypeObject); ResourceType resourceType = tx.convert(discriminator, resourceTypeObject, ResourceType.class); Resource ret = new Resource(blueprint.getName(), parentPath.extend(Resource.SEGMENT_TYPE, tx.extractId(entity)).get(), null, null, null, resourceType, blueprint.getProperties()); Relationship definesRel = new Relationship(tx.extractId(r), defines.name(), resourceTypePath, entityPath); List<Notification<?, ?>> notifications = new ArrayList<>(); notifications.add(new Notification<>(definesRel, definesRel, created())); if (tx.extractType(parent).equals(Resource.class)) { //we're creating a child resource... need to also create the implicit isParentOf //in here, we do use the relationship rules to check if the hierarchy we're introducing by this call //conforms to the rules. String relationshipName = isParentOf.name(); RelationshipRules.checkCreate(discriminator, tx, parent, outgoing, relationshipName, entity); r = tx.relate(discriminator, parent, entity, relationshipName, null); Relationship parentRel = new Relationship(tx.extractId(r), isParentOf.name(), parentPath, entityPath); notifications.add(new Notification<>(parentRel, parentRel, created())); } return new EntityAndPendingNotifications<>(entity, ret, notifications); } @Override public Resources.Multiple getAll(Filter[][] filters) { return new Multiple<>(context.proceed().whereAll(filters).get()); } @Override public Resources.Single get(String id) throws EntityNotFoundException { return new Single<>(context.proceed().where(id(id)).get()); } @Override public Resources.Single create(Resource.Blueprint blueprint, boolean cache) throws EntityAlreadyExistsException { if (blueprint.getResourceTypePath() == null) { throw new IllegalArgumentException("ResourceType path is null"); } return new Single<>(context.toCreatedEntity(doCreate(blueprint), cache)); } } public static class ReadContained<BE> extends Traversal<BE, Resource> implements Resources.ReadContained { public ReadContained(TraversalContext<BE, Resource> context) { super(context); } @Override public Resources.Multiple getAll(Filter[][] filters) { return new Multiple<>(context.proceed().whereAll(filters).get()); } @Override public Resources.Single get(String id) throws EntityNotFoundException { return new Single<>(context.proceed().where(id(id)).get()); } } public static class Read<BE> extends Traversal<BE, Resource> implements Resources.Read { public Read(TraversalContext<BE, Resource> context) { super(context); } @Override public Resources.Multiple getAll(Filter[][] filters) { return new Multiple<>(context.proceed().whereAll(filters).get()); } @Override public Resources.Single get(Path id) throws EntityNotFoundException { return new Single<>(context.proceedTo(id)); } } public static class ReadAssociate<BE> extends Read<BE> implements Resources.ReadAssociate { public ReadAssociate(TraversalContext<BE, Resource> context) { super(context); } @Override public Relationship associate(Path id) throws EntityNotFoundException, RelationAlreadyExistsException { return Associator.associate(context, SegmentType.r, isParentOf, id); } @Override public Relationship disassociate(Path id) throws EntityNotFoundException, IllegalArgumentException { return Associator.disassociate(context, SegmentType.r, isParentOf, id); } @Override public Relationship associationWith(Path path) throws RelationNotFoundException { return Associator.associationWith(context, SegmentType.r, isParentOf, path); } } public static class Single<BE> extends SingleSyncedFetcher<BE, Resource, Resource.Blueprint, Resource.Update> implements Resources.Single { public Single(TraversalContext<BE, Resource> context) { super(context); } @Override public Metrics.ReadWrite metrics() { return new BaseMetrics.ReadWrite<>(context.proceedTo(contains, Metric.class).get()); } @Override public Metrics.ReadAssociate allMetrics() { return new BaseMetrics.ReadAssociate<>(context.proceedTo(incorporates, Metric.class).get()); } @Override public Resources.ReadAssociate allResources() { return new ReadAssociate<>(context.proceed().hop(Related.by(isParentOf), With.type( Resource.class)).get()); } @Override public Resources.ReadWrite resources() { return new ReadWrite<>(context.proceed().hop(Related.by(contains), With.type( Resource.class)).get()); } @Override public Resources.Read recursiveResources() { return new Read<>(context.proceed().hop(RecurseFilter.builder().addChain(Related.by(contains), With .type(Resource.class)).build()).get()); } @Override public Resources.Single parent() { return new Single<>(context.proceed().hop(Related.asTargetBy(contains), With.type( Resource.class)).get()); } @Override public Resources.Read parents() { return new Read<>(context.proceed().hop(Related.asTargetBy(isParentOf), With.type( Resource.class)).get()); } @Override public Data.ReadWrite<DataRole.Resource> data() { return new BaseData.ReadWrite<>(context.proceedTo(contains, DataEntity.class).get(), DataRole.Resource.class, BaseData.DataModificationChecks.<BE>none()); } } public static class Multiple<BE> extends MultipleEntityFetcher<BE, Resource, Resource.Update> implements Resources.Multiple { public Multiple(TraversalContext<BE, Resource> context) { super(context); } @Override public Metrics.Read metrics() { return new BaseMetrics.Read<>(context.proceedTo(contains, Metric.class).get()); } @Override public Metrics.Read allMetrics() { return new BaseMetrics.Read<>(context.proceedTo(incorporates, Metric.class).get()); } @Override public Resources.Read allResources() { return new ReadAssociate<>(context.proceed().hop(Related.by(isParentOf), With.type( Resource.class)).get()); } @Override public Resources.Read recursiveResources() { return new Read<>(context.proceed().hop(RecurseFilter.builder().addChain(Related.by(isParentOf), With .type(Resource.class)).build()).get()); } @Override public Resources.ReadWrite resources() { return new ReadWrite<>(context.proceed().hop(Related.by(contains), With.type( Resource.class)).get()); } @Override public Resources.Read parents() { return new Read<>(context.proceed().hop(Related.asTargetBy(isParentOf), With.type( Resource.class)).get()); } @Override public Data.Read<DataRole.Resource> data() { return new BaseData.Read<>(context.proceedTo(contains, DataEntity.class).get(), BaseData.DataModificationChecks.<BE>none()); } } }