Sho SHIMIZU

Separate ResourceStore into stores for discrete and continuous type

This is a preliminary work for ONOS-4281.

Change-Id: Ifed9c761eb16f6a249a9d069948edc7421301617
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.resource.impl;
17 +
18 +import com.google.common.collect.ImmutableList;
19 +import com.google.common.collect.ImmutableSet;
20 +import com.google.common.collect.Maps;
21 +import org.onlab.util.GuavaCollectors;
22 +import org.onlab.util.Tools;
23 +import org.onosproject.net.resource.ContinuousResource;
24 +import org.onosproject.net.resource.ContinuousResourceId;
25 +import org.onosproject.net.resource.DiscreteResourceId;
26 +import org.onosproject.net.resource.Resource;
27 +import org.onosproject.net.resource.ResourceAllocation;
28 +import org.onosproject.net.resource.ResourceConsumer;
29 +import org.onosproject.store.service.ConsistentMap;
30 +import org.onosproject.store.service.ConsistentMapException;
31 +import org.onosproject.store.service.StorageService;
32 +import org.onosproject.store.service.TransactionContext;
33 +import org.onosproject.store.service.Versioned;
34 +
35 +import java.util.LinkedHashSet;
36 +import java.util.List;
37 +import java.util.Set;
38 +import java.util.stream.Stream;
39 +
40 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.ContinuousResourceAllocation;
41 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.MAX_RETRIES;
42 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.RETRY_DELAY;
43 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
44 +import static org.onosproject.store.resource.impl.ResourceStoreUtil.hasEnoughResource;
45 +
46 +class ConsistentContinuousResourceStore {
47 + private ConsistentMap<ContinuousResourceId, ContinuousResourceAllocation> consumers;
48 + private ConsistentMap<DiscreteResourceId, Set<ContinuousResource>> childMap;
49 +
50 + ConsistentContinuousResourceStore(StorageService service) {
51 + this.consumers = service.<ContinuousResourceId, ContinuousResourceAllocation>consistentMapBuilder()
52 + .withName(MapNames.CONTINUOUS_CONSUMER_MAP)
53 + .withSerializer(SERIALIZER)
54 + .build();
55 + this.childMap = service.<DiscreteResourceId, Set<ContinuousResource>>consistentMapBuilder()
56 + .withName(MapNames.CONTINUOUS_CHILD_MAP)
57 + .withSerializer(SERIALIZER)
58 + .build();
59 +
60 + Tools.retryable(() -> childMap.put(Resource.ROOT.id(), new LinkedHashSet<>()),
61 + ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY);
62 + }
63 +
64 + TransactionalContinuousResourceStore transactional(TransactionContext tx) {
65 + return new TransactionalContinuousResourceStore(tx);
66 + }
67 +
68 + // computational complexity: O(n) where n is the number of the existing allocations for the resource
69 + List<ResourceAllocation> getResourceAllocations(ContinuousResourceId resource) {
70 + Versioned<ContinuousResourceAllocation> allocations = consumers.get(resource);
71 + if (allocations == null) {
72 + return ImmutableList.of();
73 + }
74 +
75 + return allocations.value().allocations().stream()
76 + .filter(x -> x.resource().id().equals(resource))
77 + .collect(GuavaCollectors.toImmutableList());
78 + }
79 +
80 + Set<ContinuousResource> getChildResources(DiscreteResourceId parent) {
81 + Versioned<Set<ContinuousResource>> children = childMap.get(parent);
82 +
83 + if (children == null) {
84 + return ImmutableSet.of();
85 + }
86 +
87 + return children.value();
88 + }
89 +
90 + public boolean isAvailable(ContinuousResource resource) {
91 + // check if it's registered or not.
92 + Versioned<Set<ContinuousResource>> children = childMap.get(resource.parent().get().id());
93 + if (children == null) {
94 + return false;
95 + }
96 +
97 + ContinuousResource registered = children.value().stream()
98 + .filter(c -> c.id().equals(resource.id()))
99 + .findFirst()
100 + .get();
101 + if (registered.value() < resource.value()) {
102 + // Capacity < requested, can never satisfy
103 + return false;
104 + }
105 +
106 + // check if there's enough left
107 + Versioned<ContinuousResourceAllocation> allocation = consumers.get(resource.id());
108 + if (allocation == null) {
109 + // no allocation (=no consumer) full registered resources available
110 + return true;
111 + }
112 +
113 + return hasEnoughResource(allocation.value().original(), resource, allocation.value());
114 + }
115 +
116 + <T> Stream<ContinuousResource> getAllocatedResources(DiscreteResourceId parent, Class<T> cls) {
117 + Set<ContinuousResource> children = getChildResources(parent);
118 + if (children.isEmpty()) {
119 + return Stream.of();
120 + }
121 +
122 + return children.stream()
123 + .filter(x -> x.id().equals(parent.child(cls)))
124 + // we don't use cascading simple predicates like follows to reduce accesses to consistent map
125 + // .filter(x -> continuousConsumers.containsKey(x.id()))
126 + // .filter(x -> continuousConsumers.get(x.id()) != null)
127 + // .filter(x -> !continuousConsumers.get(x.id()).value().allocations().isEmpty());
128 + .filter(resource -> {
129 + Versioned<ContinuousResourceAllocation> allocation = consumers.get(resource.id());
130 + if (allocation == null) {
131 + return false;
132 + }
133 + return !allocation.value().allocations().isEmpty();
134 + });
135 + }
136 +
137 + Stream<ContinuousResource> getResources(ResourceConsumer consumer) {
138 + return consumers.values().stream()
139 + .flatMap(x -> x.value().allocations().stream()
140 + .map(y -> Maps.immutableEntry(x.value().original(), y)))
141 + .filter(x -> x.getValue().consumer().equals(consumer))
142 + .map(x -> x.getKey());
143 + }
144 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.resource.impl;
17 +
18 +import com.google.common.collect.ImmutableList;
19 +import com.google.common.collect.ImmutableSet;
20 +import org.onlab.util.Tools;
21 +import org.onosproject.net.resource.DiscreteResource;
22 +import org.onosproject.net.resource.DiscreteResourceId;
23 +import org.onosproject.net.resource.Resource;
24 +import org.onosproject.net.resource.ResourceAllocation;
25 +import org.onosproject.net.resource.ResourceConsumer;
26 +import org.onosproject.net.resource.Resources;
27 +import org.onosproject.store.service.ConsistentMap;
28 +import org.onosproject.store.service.ConsistentMapException;
29 +import org.onosproject.store.service.StorageService;
30 +import org.onosproject.store.service.TransactionContext;
31 +import org.onosproject.store.service.Versioned;
32 +
33 +import java.util.LinkedHashSet;
34 +import java.util.List;
35 +import java.util.Map;
36 +import java.util.Set;
37 +import java.util.stream.Stream;
38 +
39 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.MAX_RETRIES;
40 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.RETRY_DELAY;
41 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
42 +
43 +class ConsistentDiscreteResourceStore {
44 + private ConsistentMap<DiscreteResourceId, ResourceConsumer> consumers;
45 + private ConsistentMap<DiscreteResourceId, Set<DiscreteResource>> childMap;
46 +
47 + ConsistentDiscreteResourceStore(StorageService service) {
48 + this.consumers = service.<DiscreteResourceId, ResourceConsumer>consistentMapBuilder()
49 + .withName(MapNames.DISCRETE_CONSUMER_MAP)
50 + .withSerializer(SERIALIZER)
51 + .build();
52 + this.childMap = service.<DiscreteResourceId, Set<DiscreteResource>>consistentMapBuilder()
53 + .withName(MapNames.DISCRETE_CHILD_MAP)
54 + .withSerializer(SERIALIZER)
55 + .build();
56 +
57 + Tools.retryable(() -> childMap.put(Resource.ROOT.id(), new LinkedHashSet<>()),
58 + ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY);
59 + }
60 +
61 + TransactionalDiscreteResourceStore transactional(TransactionContext tx) {
62 + return new TransactionalDiscreteResourceStore(tx);
63 + }
64 +
65 + // computational complexity: O(1)
66 + List<ResourceAllocation> getResourceAllocations(DiscreteResourceId resource) {
67 + Versioned<ResourceConsumer> consumer = consumers.get(resource);
68 + if (consumer == null) {
69 + return ImmutableList.of();
70 + }
71 +
72 + return ImmutableList.of(new ResourceAllocation(Resources.discrete(resource).resource(), consumer.value()));
73 + }
74 +
75 + Set<DiscreteResource> getChildResources(DiscreteResourceId parent) {
76 + Versioned<Set<DiscreteResource>> children = childMap.get(parent);
77 +
78 + if (children == null) {
79 + return ImmutableSet.of();
80 + }
81 +
82 + return children.value();
83 + }
84 +
85 + boolean isAvailable(DiscreteResource resource) {
86 + return getResourceAllocations(resource.id()).isEmpty();
87 + }
88 +
89 + <T> Stream<DiscreteResource> getAllocatedResources(DiscreteResourceId parent, Class<T> cls) {
90 + Set<DiscreteResource> children = getChildResources(parent);
91 + if (children.isEmpty()) {
92 + return Stream.of();
93 + }
94 +
95 + return children.stream()
96 + .filter(x -> x.isTypeOf(cls))
97 + .filter(x -> consumers.containsKey(x.id()));
98 + }
99 +
100 + Stream<DiscreteResource> getResources(ResourceConsumer consumer) {
101 + return consumers.entrySet().stream()
102 + .filter(x -> x.getValue().value().equals(consumer))
103 + .map(Map.Entry::getKey)
104 + .map(x -> Resources.discrete(x).resource());
105 + }
106 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.resource.impl;
17 +
18 +final class MapNames {
19 + static final String DISCRETE_CONSUMER_MAP = "onos-discrete-consumers";
20 + static final String DISCRETE_CHILD_MAP = "onos-resource-discrete-children";
21 + static final String CONTINUOUS_CONSUMER_MAP = "onos-continuous-consumers";
22 + static final String CONTINUOUS_CHILD_MAP = "onos-resource-continuous-children";
23 +
24 + // prohibit contruction
25 + private MapNames() {}
26 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.resource.impl;
17 +
18 +import org.onosproject.net.resource.ContinuousResource;
19 +
20 +final class ResourceStoreUtil {
21 + // prohibit construction
22 + private ResourceStoreUtil() {}
23 +
24 + /**
25 + * Checks if there is enough resource volume to allocated the requested resource
26 + * against the specified resource.
27 + *
28 + * @param original original resource
29 + * @param request requested resource
30 + * @param allocation current allocation of the resource
31 + * @return true if there is enough resource volume. Otherwise, false.
32 + */
33 + // computational complexity: O(n) where n is the number of allocations
34 + static boolean hasEnoughResource(ContinuousResource original,
35 + ContinuousResource request,
36 + ConsistentResourceStore.ContinuousResourceAllocation allocation) {
37 + if (allocation == null) {
38 + return request.value() <= original.value();
39 + }
40 +
41 + double allocated = allocation.allocations().stream()
42 + .filter(x -> x.resource() instanceof ContinuousResource)
43 + .map(x -> (ContinuousResource) x.resource())
44 + .mapToDouble(ContinuousResource::value)
45 + .sum();
46 + double left = original.value() - allocated;
47 + return request.value() <= left;
48 + }
49 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.resource.impl;
17 +
18 +import com.google.common.collect.ImmutableList;
19 +import com.google.common.collect.Sets;
20 +import org.onlab.util.GuavaCollectors;
21 +import org.onosproject.net.resource.ContinuousResource;
22 +import org.onosproject.net.resource.ContinuousResourceId;
23 +import org.onosproject.net.resource.DiscreteResourceId;
24 +import org.onosproject.net.resource.Resource;
25 +import org.onosproject.net.resource.ResourceAllocation;
26 +import org.onosproject.net.resource.ResourceConsumer;
27 +import org.onosproject.store.resource.impl.ConsistentResourceStore.ContinuousResourceAllocation;
28 +import org.onosproject.store.service.TransactionContext;
29 +import org.onosproject.store.service.TransactionalMap;
30 +import org.slf4j.Logger;
31 +import org.slf4j.LoggerFactory;
32 +
33 +import java.util.LinkedHashSet;
34 +import java.util.List;
35 +import java.util.Optional;
36 +import java.util.Set;
37 +import java.util.stream.Collectors;
38 +
39 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
40 +import static org.onosproject.store.resource.impl.ResourceStoreUtil.hasEnoughResource;
41 +
42 +class TransactionalContinuousResourceStore {
43 + private final Logger log = LoggerFactory.getLogger(getClass());
44 + private final TransactionalMap<DiscreteResourceId, Set<ContinuousResource>> childMap;
45 + private final TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> consumers;
46 +
47 + TransactionalContinuousResourceStore(TransactionContext tx) {
48 + this.childMap = tx.getTransactionalMap(MapNames.CONTINUOUS_CHILD_MAP, SERIALIZER);
49 + this.consumers = tx.getTransactionalMap(MapNames.CONTINUOUS_CONSUMER_MAP, SERIALIZER);
50 + }
51 +
52 + // iterate over the values in the set: O(n) operation
53 + Optional<Resource> lookup(ContinuousResourceId id) {
54 + if (!id.parent().isPresent()) {
55 + return Optional.of(Resource.ROOT);
56 + }
57 +
58 + Set<ContinuousResource> values = childMap.get(id.parent().get());
59 + if (values == null) {
60 + return Optional.empty();
61 + }
62 +
63 + return values.stream()
64 + .filter(x -> x.id().equals(id))
65 + .map(x -> (Resource) x)
66 + .findFirst();
67 + }
68 +
69 + boolean appendValues(DiscreteResourceId key, List<ContinuousResource> values) {
70 + Set<ContinuousResource> requested = new LinkedHashSet<>(values);
71 + Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, requested);
72 + if (oldValues == null) {
73 + return true;
74 + }
75 +
76 + Set<ContinuousResource> addedValues = Sets.difference(requested, oldValues);
77 + // no new value, then no-op
78 + if (addedValues.isEmpty()) {
79 + // don't write to map because all values are already stored
80 + return true;
81 + }
82 +
83 + Set<ContinuousResourceId> addedIds = addedValues.stream()
84 + .map(ContinuousResource::id)
85 + .collect(Collectors.toSet());
86 + // if the value is not found but the same ID is found
87 + // (this happens only when being continuous resource)
88 + if (oldValues.stream().anyMatch(x -> addedIds.contains(x.id()))) {
89 + // no-op, but indicating failure (reject the request)
90 + return false;
91 + }
92 + Set<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
93 + newValues.addAll(addedValues);
94 + return childMap.replace(key, oldValues, newValues);
95 + }
96 +
97 + boolean removeValues(DiscreteResourceId key, List<ContinuousResource> values) {
98 + Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>());
99 + if (oldValues == null) {
100 + log.trace("No-Op removing values. key {} did not exist", key);
101 + return true;
102 + }
103 +
104 + if (values.stream().allMatch(x -> !oldValues.contains(x))) {
105 + // don't write map because none of the values are stored
106 + log.trace("No-Op removing values. key {} did not contain {}", key, values);
107 + return true;
108 + }
109 +
110 + LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
111 + newValues.removeAll(values);
112 + return childMap.replace(key, oldValues, newValues);
113 + }
114 +
115 + boolean isAllocated(ContinuousResourceId id) {
116 + ContinuousResourceAllocation allocations = consumers.get(id);
117 + return allocations != null && !allocations.allocations().isEmpty();
118 + }
119 +
120 + boolean allocate(ResourceConsumer consumer, ContinuousResource request) {
121 + // if the resource is not registered, then abort
122 + Optional<Resource> lookedUp = lookup(request.id());
123 + if (!lookedUp.isPresent()) {
124 + return false;
125 + }
126 + // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
127 + ContinuousResource original = (ContinuousResource) lookedUp.get();
128 + ContinuousResourceAllocation allocations = consumers.get(request.id());
129 + if (!hasEnoughResource(original, request, allocations)) {
130 + return false;
131 + }
132 +
133 + boolean success = appendValue(original, new ResourceAllocation(request, consumer));
134 + if (!success) {
135 + return false;
136 + }
137 +
138 + return true;
139 + }
140 +
141 + // Appends the specified ResourceAllocation to the existing values stored in the map
142 + // computational complexity: O(n) where n is the number of the elements in the associated allocation
143 + private boolean appendValue(ContinuousResource original, ResourceAllocation value) {
144 + ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(),
145 + new ContinuousResourceAllocation(original, ImmutableList.of(value)));
146 + if (oldValue == null) {
147 + return true;
148 + }
149 +
150 + if (oldValue.allocations().contains(value)) {
151 + // don't write to map because all values are already stored
152 + return true;
153 + }
154 +
155 + ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original,
156 + ImmutableList.<ResourceAllocation>builder()
157 + .addAll(oldValue.allocations())
158 + .add(value)
159 + .build());
160 + return consumers.replace(original.id(), oldValue, newValue);
161 + }
162 +
163 + boolean release(ContinuousResource resource, ResourceConsumer consumer) {
164 + ContinuousResourceAllocation oldAllocation = consumers.get(resource.id());
165 + ImmutableList<ResourceAllocation> newAllocations = oldAllocation.allocations().stream()
166 + .filter(x -> !(x.consumer().equals(consumer) &&
167 + ((ContinuousResource) x.resource()).value() == resource.value()))
168 + .collect(GuavaCollectors.toImmutableList());
169 +
170 + if (!consumers.replace(resource.id(), oldAllocation,
171 + new ContinuousResourceAllocation(oldAllocation.original(), newAllocations))) {
172 + return false;
173 + }
174 +
175 + return true;
176 + }
177 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.resource.impl;
17 +
18 +import com.google.common.collect.Sets;
19 +import org.onosproject.net.resource.DiscreteResource;
20 +import org.onosproject.net.resource.DiscreteResourceId;
21 +import org.onosproject.net.resource.Resource;
22 +import org.onosproject.net.resource.ResourceConsumer;
23 +import org.onosproject.net.resource.Resources;
24 +import org.onosproject.store.service.TransactionContext;
25 +import org.onosproject.store.service.TransactionalMap;
26 +import org.slf4j.Logger;
27 +import org.slf4j.LoggerFactory;
28 +
29 +import java.util.LinkedHashSet;
30 +import java.util.List;
31 +import java.util.Optional;
32 +import java.util.Set;
33 +
34 +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
35 +
36 +class TransactionalDiscreteResourceStore {
37 + private final Logger log = LoggerFactory.getLogger(getClass());
38 + private final TransactionalMap<DiscreteResourceId, Set<DiscreteResource>> childMap;
39 + private final TransactionalMap<DiscreteResourceId, ResourceConsumer> consumers;
40 +
41 + TransactionalDiscreteResourceStore(TransactionContext tx) {
42 + this.childMap = tx.getTransactionalMap(MapNames.DISCRETE_CHILD_MAP, SERIALIZER);
43 + this.consumers = tx.getTransactionalMap(MapNames.DISCRETE_CONSUMER_MAP, SERIALIZER);
44 + }
45 +
46 + // check the existence in the set: O(1) operation
47 + Optional<Resource> lookup(DiscreteResourceId id) {
48 + if (!id.parent().isPresent()) {
49 + return Optional.of(Resource.ROOT);
50 + }
51 +
52 + Set<DiscreteResource> values = childMap.get(id.parent().get());
53 + if (values == null) {
54 + return Optional.empty();
55 + }
56 +
57 + DiscreteResource resource = Resources.discrete(id).resource();
58 + if (values.contains(resource)) {
59 + return Optional.of(resource);
60 + } else {
61 + return Optional.empty();
62 + }
63 + }
64 +
65 + boolean appendValues(DiscreteResourceId key, List<DiscreteResource> values) {
66 + Set<DiscreteResource> requested = new LinkedHashSet<>(values);
67 + Set<DiscreteResource> oldValues = childMap.putIfAbsent(key, requested);
68 + if (oldValues == null) {
69 + return true;
70 + }
71 +
72 + Set<DiscreteResource> addedValues = Sets.difference(requested, oldValues);
73 + // no new value, then no-op
74 + if (addedValues.isEmpty()) {
75 + // don't write to map because all values are already stored
76 + return true;
77 + }
78 +
79 + Set<DiscreteResource> newValues = new LinkedHashSet<>(oldValues);
80 + newValues.addAll(addedValues);
81 + return childMap.replace(key, oldValues, newValues);
82 + }
83 +
84 + boolean removeValues(DiscreteResourceId key, List<DiscreteResource> values) {
85 + Set<DiscreteResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>());
86 + if (oldValues == null) {
87 + log.trace("No-Op removing values. key {} did not exist", key);
88 + return true;
89 + }
90 +
91 + if (values.stream().allMatch(x -> !oldValues.contains(x))) {
92 + // don't write map because none of the values are stored
93 + log.trace("No-Op removing values. key {} did not contain {}", key, values);
94 + return true;
95 + }
96 +
97 + LinkedHashSet<DiscreteResource> newValues = new LinkedHashSet<>(oldValues);
98 + newValues.removeAll(values);
99 + return childMap.replace(key, oldValues, newValues);
100 + }
101 +
102 + boolean isAllocated(DiscreteResourceId id) {
103 + return consumers.get(id) != null;
104 + }
105 +
106 + boolean allocate(ResourceConsumer consumer, DiscreteResource resource) {
107 + // if the resource is not registered, then abort
108 + Optional<Resource> lookedUp = lookup(resource.id());
109 + if (!lookedUp.isPresent()) {
110 + return false;
111 + }
112 +
113 + ResourceConsumer oldValue = consumers.put(resource.id(), consumer);
114 + return oldValue == null;
115 + }
116 +
117 + boolean release(DiscreteResource resource, ResourceConsumer consumer) {
118 + // if this single release fails (because the resource is allocated to another consumer)
119 + // the whole release fails
120 + if (!consumers.remove(resource.id(), consumer)) {
121 + return false;
122 + }
123 +
124 + return true;
125 + }
126 +}