/* * 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 java.util.Collections.emptyList; import static org.hawkular.inventory.api.Relationships.WellKnown.contains; import static org.hawkular.inventory.api.Relationships.WellKnown.incorporates; import static org.hawkular.inventory.api.filters.Related.asTargetBy; import static org.hawkular.inventory.api.filters.With.id; import org.hawkular.inventory.api.Data; import org.hawkular.inventory.api.EntityAlreadyExistsException; import org.hawkular.inventory.api.EntityNotFoundException; import org.hawkular.inventory.api.OperationTypes; import org.hawkular.inventory.api.Query; import org.hawkular.inventory.api.filters.Filter; import org.hawkular.inventory.api.filters.With; import org.hawkular.inventory.api.model.DataEntity; import org.hawkular.inventory.api.model.MetadataPack; import org.hawkular.inventory.api.model.OperationType; 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; /** * @author Lukas Krejci * @since 0.4.0 */ public final class BaseOperationTypes { private BaseOperationTypes() { } public static class ReadWrite<BE> extends Mutator<BE, OperationType, OperationType.Blueprint, OperationType.Update, String> implements OperationTypes.ReadWrite { public ReadWrite(TraversalContext<BE, OperationType> context) { super(context); } @Override protected String getProposedId(Transaction<BE> tx, OperationType.Blueprint blueprint) { return blueprint.getId(); } @Override protected EntityAndPendingNotifications<BE, OperationType> wireUpNewEntity(Discriminator discriminator, BE entity, OperationType.Blueprint blueprint, CanonicalPath parentPath, BE parent, Transaction<BE> tx) { return new EntityAndPendingNotifications<>(entity, new OperationType(blueprint.getName(), parentPath.extend(OperationType.SEGMENT_TYPE, tx.extractId(entity)).get(), null, null, null, blueprint.getProperties()), emptyList()); } @Override public OperationTypes.Multiple getAll(Filter[][] filters) { return new BaseOperationTypes.Multiple<>(context.proceed().whereAll(filters).get()); } @Override public OperationTypes.Single get(String id) throws EntityNotFoundException { return new BaseOperationTypes.Single<>(context.proceed().where(id(id)).get()); } @Override public OperationTypes.Single create(OperationType.Blueprint blueprint, boolean cache) throws EntityAlreadyExistsException { return new BaseOperationTypes.Single<>(context.toCreatedEntity(doCreate(blueprint), cache)); } @Override protected void preCreate(OperationType.Blueprint blueprint, Transaction<BE> tx) { //disallow this if the parent resource type is a part of a metadata pack if (tx.traverseToSingle(context.discriminator(), getParent(tx), Query.path().with(asTargetBy(incorporates), With.type(MetadataPack.class)).get()) != null) { throw new IllegalArgumentException("Cannot create an operation type of resource type included in " + "a meta data pack. This would invalidate the metadata pack's identity."); } } @Override protected void preDelete(String s, BE entityRepresentation, Transaction<BE> tx) { if (isResourceTypeInMetadataPack(entityRepresentation, context.discriminator(), tx)) { throw new IllegalArgumentException("Cannot delete an operation type of resource type included in " + "a meta data pack. This would invalidate the metadata pack's identity."); } } private static <BE> boolean isResourceTypeInMetadataPack(BE operationType, Discriminator discriminator, Transaction<BE> tx) { return tx.traverseToSingle(discriminator, operationType, Query.path().with(asTargetBy(contains), asTargetBy(incorporates), With.type(MetadataPack.class)).get()) != null; } } public static class ReadContained<BE> extends Fetcher<BE, OperationType, OperationType.Update> implements OperationTypes.ReadContained { public ReadContained(TraversalContext<BE, OperationType> context) { super(context); } @Override public OperationTypes.Multiple getAll(Filter[][] filters) { return new BaseOperationTypes.Multiple<>(context.proceed().whereAll(filters).get()); } @Override public OperationTypes.Single get(String id) throws EntityNotFoundException { return new BaseOperationTypes.Single<>(context.proceed().where(id(id)).get()); } } public static class Single<BE> extends SingleSyncedFetcher<BE, OperationType, OperationType.Blueprint, OperationType.Update> implements OperationTypes.Single { public Single(TraversalContext<BE, OperationType> context) { super(context); } @Override public Data.ReadWrite<DataRole.OperationType> data() { return new BaseData.ReadWrite<>(context.proceedTo(contains, DataEntity.class).get(), DataRole.OperationType.class, new OperationTypeDataModificationChecks<>(context)); } @Override protected void preDelete(BE deletedEntity, Transaction<BE> transaction) { if (ReadWrite.isResourceTypeInMetadataPack(deletedEntity, context.discriminator(), transaction)) { throw new IllegalArgumentException("Cannot delete an operation type of resource type included in " + "a meta data pack. This would invalidate the metadata pack's identity."); } } } public static class Multiple<BE> extends MultipleEntityFetcher<BE, OperationType, OperationType.Update> implements OperationTypes.Multiple { public Multiple(TraversalContext<BE, OperationType> context) { super(context); } @Override public Data.Read<DataRole.OperationType> data() { return new BaseData.Read<>(context.proceedTo(contains, DataEntity.class).get(), new OperationTypeDataModificationChecks<>(context)); } } static class OperationTypeDataModificationChecks<BE> implements BaseData.DataModificationChecks<BE> { private final TraversalContext<BE, ?> context; OperationTypeDataModificationChecks(TraversalContext<BE, ?> context) { this.context = context; } @Override public void preCreate(DataEntity.Blueprint blueprint, Transaction<BE> transaction) { BE mp = transaction.querySingle(context.discriminator(), context.select().path().with(asTargetBy(contains), asTargetBy(incorporates), With.type(MetadataPack.class)).get()); if (mp != null) { BE ot = transaction.querySingle(context.discriminator(), context.select().get()); throw new IllegalArgumentException( "Data '" + blueprint.getId() + "' cannot be created" + " under operation type " + transaction.extractCanonicalPath(ot) + ", because the owning resource type is part of a meta data pack." + " Doing this would invalidate meta data pack's identity."); } } @Override public void preUpdate(BE dataEntity, DataEntity.Update update, Transaction<BE> tx) { if (update.getValue() == null) { return; } BE mp = tx.traverseToSingle(context.discriminator(), dataEntity, Query.path().with( asTargetBy(contains), //up to operation type asTargetBy(contains), //up to resource type asTargetBy(incorporates), With.type(MetadataPack.class) // up to the pack ).get()); if (mp != null) { CanonicalPath dataPath = tx.extractCanonicalPath(dataEntity); throw new IllegalArgumentException( "Data '" + dataPath.getSegment().getElementId() + "' cannot be updated" + " under operation type " + dataPath.up() + ", because the owning resource type is part of a meta data pack." + " Doing this would invalidate meta data pack's identity."); } } @Override public void postUpdate(BE dataEntity, Transaction<BE> tx) { } @Override public void preDelete(BE dataEntity, Transaction<BE> tx) { CanonicalPath dataPath = tx.extractCanonicalPath(dataEntity); BE ot = null; try { ot = tx.find(context.discriminator(), dataPath.up()); } catch (ElementNotFoundException e) { Fetcher.throwNotFoundException(context); } if (ReadWrite.isResourceTypeInMetadataPack(ot, context.discriminator(), tx)) { throw new IllegalArgumentException( "Data '" + dataPath.getSegment().getElementId() + "' cannot be deleted" + " under operation type " + dataPath.up() + ", because the owning resource type is part of a meta data pack." + " Doing this would invalidate meta data pack's identity."); } } @Override public void postCreate(BE dataEntity, Transaction<BE> tx) { } @Override public void postDelete(BE dataEntity, Transaction<BE> tx) { } } }