/*
* 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.deleted;
import static org.hawkular.inventory.api.Action.updated;
import java.util.Map;
import org.hawkular.inventory.api.Action;
import org.hawkular.inventory.api.EntityNotFoundException;
import org.hawkular.inventory.api.Environments;
import org.hawkular.inventory.api.Feeds;
import org.hawkular.inventory.api.MetricTypes;
import org.hawkular.inventory.api.Metrics;
import org.hawkular.inventory.api.Query;
import org.hawkular.inventory.api.QueryFragment;
import org.hawkular.inventory.api.RelationNotFoundException;
import org.hawkular.inventory.api.Relationships;
import org.hawkular.inventory.api.ResourceTypes;
import org.hawkular.inventory.api.Resources;
import org.hawkular.inventory.api.Tenants;
import org.hawkular.inventory.api.filters.RelationFilter;
import org.hawkular.inventory.api.filters.RelationWith;
import org.hawkular.inventory.api.filters.SwitchElementType;
import org.hawkular.inventory.api.model.Entity;
import org.hawkular.inventory.api.model.Environment;
import org.hawkular.inventory.api.model.Feed;
import org.hawkular.inventory.api.model.Metric;
import org.hawkular.inventory.api.model.MetricType;
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.api.model.Tenant;
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.Path;
/**
* @author Lukas Krejci
* @since 0.1.0
*/
public final class BaseRelationships {
private BaseRelationships() {
}
public static class ReadWrite<BE> extends Traversal<BE, Relationship> implements Relationships.ReadWrite {
private final Relationships.Direction direction;
private final Class<? extends Entity<?, ?>> originEntityType;
public ReadWrite(TraversalContext<BE, Relationship> context, Class<? extends Entity<?, ?>> originEntityType) {
super(context);
QueryFragment[] filters = context.selectCandidates.getFragments();
direction = ((SwitchElementType) filters[filters.length - 1].getFilter()).getDirection();
this.originEntityType = originEntityType;
}
@Override
public Relationships.Multiple named(String name) {
return new Multiple<>(direction, context.proceed().where(RelationWith.name(name)).get());
}
@Override
public Relationships.Multiple named(Relationships.WellKnown name) {
return named(name.name());
}
@Override
public Relationships.Single get(String id) throws EntityNotFoundException, RelationNotFoundException {
return new Single<>(context.proceed().where(RelationWith.id(id)).get());
}
@Override
public Relationships.Multiple getAll(RelationFilter... filters) {
return new Multiple<>(direction, context.proceed().where(filters).get());
}
@SuppressWarnings("unchecked")
@Override
public Relationships.Single linkWith(String name, Path targetOrSource, Map<String, Object> properties)
throws IllegalArgumentException {
if (null == name) {
throw new IllegalArgumentException("name was null");
}
if (null == targetOrSource) {
throw new IllegalArgumentException("targetOrSource was null");
}
return inTx(tx -> {
BE incidenceObject;
incidenceObject = Util.find(context.discriminator(), tx, context.sourcePath, targetOrSource);
BE origin = tx.querySingle(context.discriminator(), context.sourcePath);
if (origin == null) {
throw new EntityNotFoundException(originEntityType, Query.filters(context.select().get()));
}
// if this is a well-known relationship, there might be some semantic checks for it...
RelationshipRules.checkCreate(context.discriminator(), tx, origin, direction, name, incidenceObject);
EntityAndPendingNotifications<BE, Relationship> relationshipObject;
EntityAndPendingNotifications<BE, Relationship> relationshipObject2 = null;
Discriminator disc = context.discriminator();
switch (direction) {
case incoming:
relationshipObject = Util.createAssociation(disc, tx, incidenceObject, name, origin, properties);
break;
case outgoing:
relationshipObject = Util.createAssociation(disc, tx, origin, name, incidenceObject, properties);
break;
case both:
relationshipObject2 = Util.createAssociation(disc, tx, origin, name, incidenceObject, properties);
relationshipObject = Util.createAssociation(disc, tx, incidenceObject, name, origin, properties);
break;
default:
throw new AssertionError("Unhandled direction when linking. This shouldn't have happened.");
}
tx.getPreCommit().addNotifications(relationshipObject);
if (relationshipObject2 != null) {
tx.getPreCommit().addNotifications(relationshipObject2);
}
return new Single<>(context.toCreatedEntity(relationshipObject.getEntity(), true));
});
}
@Override
public void update(String id, Relationship.Update update) throws RelationNotFoundException {
//TODO this doesn't respect the current position in the graph
inTx(tx -> {
try {
BE relationshipObject = tx.find(context.discriminator(), CanonicalPath.of().relationship(id)
.get());
tx.update(context.discriminator(), relationshipObject, update);
Relationship r = tx.convert(context.discriminator(), relationshipObject, Relationship.class);
tx.getPreCommit().addNotifications(new EntityAndPendingNotifications<>(relationshipObject, r,
new Notification<>(new Action.Update<>(r, update), r, updated())));
return null;
} catch (ElementNotFoundException e) {
throw new RelationNotFoundException(id,
Query.filters(context.select().with(RelationWith.id(id)).get()));
}
});
}
@Override
public void delete(String id) throws RelationNotFoundException {
//TODO this doesn't respect the current position in the graph
inTx((tx) -> {
try {
BE relationshipObject = tx.find(context.discriminator(), CanonicalPath.of().relationship(id).get());
BE source = tx.getRelationshipSource(context.discriminator(), relationshipObject);
BE target = tx.getRelationshipTarget(context.discriminator(), relationshipObject);
String relationshipName = tx.extractRelationshipName(relationshipObject);
RelationshipRules.checkDelete(context.discriminator(), tx, source, Relationships.Direction.outgoing,
relationshipName, target);
Relationship r = tx.convert(context.discriminator(), relationshipObject, Relationship.class);
tx.markDeleted(context.discriminator(), relationshipObject);
tx.getPreCommit().addNotifications(
new EntityAndPendingNotifications<>(relationshipObject, r, deleted()));
return null;
} catch (ElementNotFoundException e) {
throw new RelationNotFoundException(id,
Query.filters(context.select().with(RelationWith.id(id)).get()));
}
});
}
}
public static class Read<BE> extends Traversal<BE, Relationship> implements Relationships.Read {
private final Relationships.Direction direction;
public Read(TraversalContext<BE, Relationship> context) {
super(context);
QueryFragment[] filters = context.selectCandidates.getFragments();
if (filters.length == 0) {
direction = Relationships.Direction.outgoing;
} else {
direction = ((SwitchElementType) filters[filters.length - 1].getFilter()).getDirection();
}
}
@Override
public Relationships.Multiple named(String name) {
return new Multiple<>(direction, context.proceed().where(RelationWith.name(name)).get());
}
@Override
public Relationships.Multiple named(Relationships.WellKnown name) {
return named(name.name());
}
@Override
public Relationships.Single get(String id) throws EntityNotFoundException, RelationNotFoundException {
return new Single<>(context.proceed().where(RelationWith.id(id)).get());
}
@Override
public Relationships.Multiple getAll(RelationFilter... filters) {
return new Multiple<>(direction, context.proceed().where(filters).get());
}
}
public static class Single<BE> extends Fetcher<BE, Relationship, Relationship.Update>
implements Relationships.Single {
public Single(TraversalContext<BE, Relationship> context) {
super(context);
}
}
public static class Multiple<BE> extends Fetcher<BE, Relationship, Relationship.Update>
implements Relationships.Multiple {
private final Relationships.Direction direction;
public Multiple(Relationships.Direction direction, TraversalContext<BE, Relationship> context) {
super(context);
this.direction = direction;
}
@Override
public Tenants.Read tenants() {
return new BaseTenants.Read<>(context.proceedFromRelationshipsTo(direction, Tenant.class).get());
}
@Override
public Environments.Read environments() {
return new BaseEnvironments.Read<>(context.proceedFromRelationshipsTo(direction, Environment.class).get());
}
@Override
public Feeds.Read feeds() {
return new BaseFeeds.Read<>(context.proceedFromRelationshipsTo(direction, Feed.class).get());
}
@Override
public MetricTypes.Read metricTypes() {
return new BaseMetricTypes.Read<>(context.proceedFromRelationshipsTo(direction, MetricType.class).get());
}
@Override
public Metrics.Read metrics() {
return new BaseMetrics.Read<>(context.proceedFromRelationshipsTo(direction, Metric.class).get());
}
@Override
public Resources.Read resources() {
return new BaseResources.Read<>(context.proceedFromRelationshipsTo(direction, Resource.class).get());
}
@Override
public ResourceTypes.Read resourceTypes() {
return new BaseResourceTypes.Read<>(context.proceedFromRelationshipsTo(direction, ResourceType.class).
get());
}
}
}