/*
* 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.defines;
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 static org.hawkular.inventory.api.filters.With.type;
import org.hawkular.inventory.api.Data;
import org.hawkular.inventory.api.EntityAlreadyExistsException;
import org.hawkular.inventory.api.EntityNotFoundException;
import org.hawkular.inventory.api.MetricTypes;
import org.hawkular.inventory.api.OperationTypes;
import org.hawkular.inventory.api.Query;
import org.hawkular.inventory.api.ResourceTypes;
import org.hawkular.inventory.api.Resources;
import org.hawkular.inventory.api.filters.Filter;
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.MetadataPack;
import org.hawkular.inventory.api.model.MetricType;
import org.hawkular.inventory.api.model.OperationType;
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;
/**
* @author Lukas Krejci
* @since 0.1.0
*/
public final class BaseResourceTypes {
private BaseResourceTypes() {
}
public static class ReadWrite<BE>
extends Mutator<BE, ResourceType, ResourceType.Blueprint, ResourceType.Update, String>
implements ResourceTypes.ReadWrite {
public ReadWrite(TraversalContext<BE, ResourceType> context) {
super(context);
}
@Override
protected String getProposedId(Transaction<BE> tx, ResourceType.Blueprint blueprint) {
return blueprint.getId();
}
@Override
protected EntityAndPendingNotifications<BE, ResourceType>
wireUpNewEntity(Discriminator discriminator, BE entity, ResourceType.Blueprint blueprint,
CanonicalPath parentPath, BE parent,
Transaction<BE> tx) {
ResourceType resourceType = new ResourceType(blueprint.getName(),
parentPath.extend(ResourceType.SEGMENT_TYPE, tx.extractId(entity)).get(), null, null, null,
blueprint.getProperties());
return new EntityAndPendingNotifications<>(entity, resourceType, emptyList());
}
@Override
public ResourceTypes.Multiple getAll(Filter[][] filters) {
return new BaseResourceTypes.Multiple<>(context.proceed().whereAll(filters).get());
}
@Override
public ResourceTypes.Single get(String id) throws EntityNotFoundException {
return new BaseResourceTypes.Single<>(context.proceed().where(id(id)).get());
}
@Override
public ResourceTypes.Single create(ResourceType.Blueprint blueprint, boolean cache) throws EntityAlreadyExistsException {
return new BaseResourceTypes.Single<>(context.toCreatedEntity(doCreate(blueprint), cache));
}
@Override
protected void preDelete(String s, BE entityRepresentation, Transaction<BE> tx) {
if (isResourceTypeInMetadataPack(entityRepresentation, context.discriminator(), tx)) {
throw new IllegalArgumentException("Cannot delete a resource type that is part of a metadata pack. " +
"This would invalidate the meta data pack's identity.");
}
}
@Override
protected void preUpdate(String s, BE entityRepresentation, ResourceType.Update update,
Transaction<BE> transaction) {
//we should check for violations of identity here if the resource type is a member of a metadata pack but
//because resource types contain no such updatable data, this is a no-op.
}
private static <BE> boolean isResourceTypeInMetadataPack(BE resourceType, Discriminator disc, Transaction<BE> tx) {
return tx.traverseToSingle(disc, resourceType, Query.path()
.with(asTargetBy(incorporates), type(MetadataPack.class)).get()) != null;
}
}
public static class ReadContained<BE> extends Fetcher<BE, ResourceType, ResourceType.Update>
implements ResourceTypes.ReadContained {
public ReadContained(TraversalContext<BE, ResourceType> context) {
super(context);
}
@Override
public ResourceTypes.Multiple getAll(Filter[][] filters) {
return new BaseResourceTypes.Multiple<>(context.proceed().whereAll(filters).get());
}
@Override
public ResourceTypes.Single get(String id) throws EntityNotFoundException {
return new BaseResourceTypes.Single<>(context.proceed().where(id(id)).get());
}
}
public static class Read<BE> extends Fetcher<BE, ResourceType, ResourceType.Update> implements ResourceTypes.Read {
public Read(TraversalContext<BE, ResourceType> context) {
super(context);
}
@Override
public ResourceTypes.Multiple getAll(Filter[][] filters) {
return new BaseResourceTypes.Multiple<>(context.proceed().whereAll(filters).get());
}
@Override
public ResourceTypes.Single get(Path id) throws EntityNotFoundException {
return new BaseResourceTypes.Single<>(context.proceedTo(id));
}
}
public static class Single<BE> extends SingleSyncedFetcher<BE, ResourceType, ResourceType.Blueprint,
ResourceType.Update> implements ResourceTypes.Single {
public Single(TraversalContext<BE, ResourceType> context) {
super(context);
}
@Override
public Resources.Read resources() {
return new BaseResources.Read<>(context.proceedTo(defines, Resource.class).get());
}
@Override
public MetricTypes.ReadAssociate metricTypes() {
return new BaseMetricTypes.ReadAssociate<>(context.proceedTo(incorporates, MetricType.class).get());
}
@Override
public OperationTypes.ReadWrite operationTypes() {
return new BaseOperationTypes.ReadWrite<>(context.proceedTo(contains, OperationType.class).get());
}
@Override
protected void preDelete(BE deletedEntity, Transaction<BE> tx) {
if (ReadWrite.isResourceTypeInMetadataPack(deletedEntity, context.discriminator(), tx)) {
throw new IllegalArgumentException("Cannot delete a resource type that is part of a metadata pack. " +
"This would invalidate the meta data pack's identity.");
}
}
@Override
protected void preUpdate(BE updatedEntity, ResourceType.Update update,
Transaction<BE> transaction) {
}
@Override
public Data.ReadWrite<DataRole.ResourceType> data() {
return new BaseData.ReadWrite<>(context.proceedTo(contains, DataEntity.class).get(),
DataRole.ResourceType.class, new ResourceTypeDataModificationChecks<>(context));
}
@Override
public ResourceTypes.Read identical() {
return new Read<>(context.proceed().hop(With.sameIdentityHash()).get());
}
}
public static class Multiple<BE> extends MultipleEntityFetcher<BE, ResourceType, ResourceType.Update>
implements ResourceTypes.Multiple {
public Multiple(TraversalContext<BE, ResourceType> context) {
super(context);
}
@Override
public Resources.Read resources() {
return new BaseResources.Read<>(context.proceedTo(defines, Resource.class).get());
}
@Override
public MetricTypes.Read metricTypes() {
return new BaseMetricTypes.Read<>(context.proceedTo(incorporates, MetricType.class).get());
}
@Override public OperationTypes.ReadContained operationTypes() {
return new BaseOperationTypes.ReadContained<>(context.proceedTo(contains, OperationType.class).get());
}
@Override
public Data.Read<DataRole.ResourceType> data() {
return new BaseData.Read<>(context.proceedTo(contains, DataEntity.class).get(),
new ResourceTypeDataModificationChecks<>(context));
}
@Override
public ResourceTypes.Read identical() {
return new Read<>(context.proceed().hop(With.sameIdentityHash()).get());
}
}
static class ResourceTypeDataModificationChecks<BE> implements BaseData.DataModificationChecks<BE> {
private final TraversalContext<BE, ?> context;
public ResourceTypeDataModificationChecks(TraversalContext<BE, ?> context) {
this.context = context;
}
@Override
public void preCreate(DataEntity.Blueprint blueprint, Transaction<BE> tx) {
BE pack = tx.querySingle(context.discriminator(), context.select().path().with(Related
.asTargetBy(incorporates), With.type(MetadataPack.class)).get());
if (pack != null) {
BE rt = tx.querySingle(context.discriminator(), context.select().get());
throw new IllegalArgumentException(
"Data '" + blueprint.getId() + "' cannot be created" +
" under resource type " + tx.extractCanonicalPath(rt) +
", because it 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 preUpdate(BE dataEntity, DataEntity.Update update, Transaction<BE> tx) {
if (update.getValue() == null) {
return;
}
BE mp = tx.traverseToSingle(context.discriminator(), dataEntity, Query.path().with(
Related.asTargetBy(contains), //up to resource type
Related.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 resource type " + dataPath.up() +
", because it 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 rt = null;
try {
rt = tx.find(context.discriminator(), dataPath.up());
} catch (ElementNotFoundException e) {
Fetcher.throwNotFoundException(context);
}
if (ReadWrite.isResourceTypeInMetadataPack(rt, context.discriminator(), tx)) {
throw new IllegalArgumentException(
"Data '" + dataPath.getSegment().getElementId() + "' cannot be deleted" +
" under resource type " + dataPath.up() +
", because it is part of a meta data pack." +
" Doing this would invalidate meta data pack's identity.");
}
}
@Override
public void postDelete(BE dataEntity, Transaction<BE> tx) {
}
}
}