Sho SHIMIZU
Committed by Gerrit Code Review

ONOS-3296: Support continuous type resources

Change-Id: I155e41e7a7c1750ff45986a55bedab353485d3fa
...@@ -126,13 +126,13 @@ public interface ResourceService extends ListenerService<ResourceEvent, Resource ...@@ -126,13 +126,13 @@ public interface ResourceService extends ListenerService<ResourceEvent, Resource
126 boolean release(ResourceConsumer consumer); 126 boolean release(ResourceConsumer consumer);
127 127
128 /** 128 /**
129 - * Returns resource allocation of the specified resource. 129 + * Returns resource allocations of the specified resource.
130 * 130 *
131 * @param resource resource to check the allocation 131 * @param resource resource to check the allocation
132 - * @return allocation information enclosed by Optional. 132 + * @return list of allocation information.
133 - * If the resource is not allocated, the return value is empty. 133 + * If the resource is not allocated, the return value is an empty list.
134 */ 134 */
135 - Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource); 135 + List<ResourceAllocation> getResourceAllocation(ResourcePath resource);
136 136
137 /** 137 /**
138 * Returns allocated resources being as children of the specified parent and being the specified resource type. 138 * Returns allocated resources being as children of the specified parent and being the specified resource type.
......
1 /* 1 /*
2 - * Copyright 2015 Open Networking Laboratory 2 + * Copyright 2015-2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -20,7 +20,6 @@ import org.onosproject.store.Store; ...@@ -20,7 +20,6 @@ import org.onosproject.store.Store;
20 20
21 import java.util.Collection; 21 import java.util.Collection;
22 import java.util.List; 22 import java.util.List;
23 -import java.util.Optional;
24 23
25 /** 24 /**
26 * Service for storing resource and consumer information. 25 * Service for storing resource and consumer information.
...@@ -77,12 +76,23 @@ public interface ResourceStore extends Store<ResourceEvent, ResourceStoreDelegat ...@@ -77,12 +76,23 @@ public interface ResourceStore extends Store<ResourceEvent, ResourceStoreDelegat
77 boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers); 76 boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers);
78 77
79 /** 78 /**
80 - * Returns the resource consumer to whom the specified resource is allocated. 79 + * Returns the resource consumers to whom the specified resource is allocated.
80 + * The return value is a list having only one element when the given resource is discrete type.
81 + * The return value may have multiple elements when the given resource is continuous type.
81 * 82 *
82 * @param resource resource whose allocated consumer to be returned 83 * @param resource resource whose allocated consumer to be returned
83 - * @return resource consumer who are allocated the resource 84 + * @return resource consumers who are allocated the resource.
85 + * Returns empty list if there is no such consumer.
84 */ 86 */
85 - Optional<ResourceConsumer> getConsumer(ResourcePath resource); 87 + List<ResourceConsumer> getConsumers(ResourcePath resource);
88 +
89 + /**
90 + * Returns the availability of the specified resource.
91 + *
92 + * @param resource resource to check the availability
93 + * @return true if available, otherwise false
94 + */
95 + boolean isAvailable(ResourcePath resource);
86 96
87 /** 97 /**
88 * Returns a collection of the resources allocated to the specified consumer. 98 * Returns a collection of the resources allocated to the specified consumer.
......
...@@ -313,9 +313,11 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu ...@@ -313,9 +313,11 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
313 OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port()); 313 OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
314 Optional<IntentId> intentId = 314 Optional<IntentId> intentId =
315 resourceService.getResourceAllocation(ResourcePath.discrete(ochCP.deviceId(), ochCP.port())) 315 resourceService.getResourceAllocation(ResourcePath.discrete(ochCP.deviceId(), ochCP.port()))
316 + .stream()
316 .map(ResourceAllocation::consumer) 317 .map(ResourceAllocation::consumer)
317 .filter(x -> x instanceof IntentId) 318 .filter(x -> x instanceof IntentId)
318 - .map(x -> (IntentId) x); 319 + .map(x -> (IntentId) x)
320 + .findAny();
319 321
320 if (isAvailable(intentId.orElse(null))) { 322 if (isAvailable(intentId.orElse(null))) {
321 return ochPort; 323 return ochPort;
...@@ -332,9 +334,12 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu ...@@ -332,9 +334,12 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
332 334
333 Optional<IntentId> intentId = 335 Optional<IntentId> intentId =
334 resourceService.getResourceAllocation(ResourcePath.discrete(oduPort.deviceId(), port.number())) 336 resourceService.getResourceAllocation(ResourcePath.discrete(oduPort.deviceId(), port.number()))
337 + .stream()
335 .map(ResourceAllocation::consumer) 338 .map(ResourceAllocation::consumer)
336 .filter(x -> x instanceof IntentId) 339 .filter(x -> x instanceof IntentId)
337 - .map(x -> (IntentId) x); 340 + .map(x -> (IntentId) x)
341 + .findAny();
342 +
338 if (isAvailable(intentId.orElse(null))) { 343 if (isAvailable(intentId.orElse(null))) {
339 return (OchPort) port; 344 return (OchPort) port;
340 } 345 }
......
1 /* 1 /*
2 - * Copyright 2015 Open Networking Laboratory 2 + * Copyright 2015-2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference; 23 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality; 24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.apache.felix.scr.annotations.Service; 25 import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.util.GuavaCollectors;
26 import org.onosproject.event.AbstractListenerManager; 27 import org.onosproject.event.AbstractListenerManager;
27 import org.onosproject.net.newresource.ResourceAdminService; 28 import org.onosproject.net.newresource.ResourceAdminService;
28 import org.onosproject.net.newresource.ResourceAllocation; 29 import org.onosproject.net.newresource.ResourceAllocation;
...@@ -34,10 +35,8 @@ import org.onosproject.net.newresource.ResourcePath; ...@@ -34,10 +35,8 @@ import org.onosproject.net.newresource.ResourcePath;
34 import org.onosproject.net.newresource.ResourceStore; 35 import org.onosproject.net.newresource.ResourceStore;
35 import org.onosproject.net.newresource.ResourceStoreDelegate; 36 import org.onosproject.net.newresource.ResourceStoreDelegate;
36 37
37 -import java.util.ArrayList;
38 import java.util.Collection; 38 import java.util.Collection;
39 import java.util.List; 39 import java.util.List;
40 -import java.util.Optional;
41 import java.util.stream.Collectors; 40 import java.util.stream.Collectors;
42 41
43 import static com.google.common.base.Preconditions.checkNotNull; 42 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -107,11 +106,13 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent ...@@ -107,11 +106,13 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent
107 } 106 }
108 107
109 @Override 108 @Override
110 - public Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource) { 109 + public List<ResourceAllocation> getResourceAllocation(ResourcePath resource) {
111 checkNotNull(resource); 110 checkNotNull(resource);
112 111
113 - Optional<ResourceConsumer> consumer = store.getConsumer(resource); 112 + List<ResourceConsumer> consumers = store.getConsumers(resource);
114 - return consumer.map(x -> new ResourceAllocation(resource, x)); 113 + return consumers.stream()
114 + .map(x -> new ResourceAllocation(resource, x))
115 + .collect(GuavaCollectors.toImmutableList());
115 } 116 }
116 117
117 @Override 118 @Override
...@@ -119,17 +120,12 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent ...@@ -119,17 +120,12 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent
119 checkNotNull(parent); 120 checkNotNull(parent);
120 checkNotNull(cls); 121 checkNotNull(cls);
121 122
122 - Collection<ResourcePath> resources = store.getAllocatedResources(parent, cls);
123 - List<ResourceAllocation> allocations = new ArrayList<>(resources.size());
124 - for (ResourcePath resource: resources) {
125 // We access store twice in this method, then the store may be updated by others 123 // We access store twice in this method, then the store may be updated by others
126 - Optional<ResourceConsumer> consumer = store.getConsumer(resource); 124 + Collection<ResourcePath> resources = store.getAllocatedResources(parent, cls);
127 - if (consumer.isPresent()) { 125 + return resources.stream()
128 - allocations.add(new ResourceAllocation(resource, consumer.get())); 126 + .flatMap(resource -> store.getConsumers(resource).stream()
129 - } 127 + .map(consumer -> new ResourceAllocation(resource, consumer)))
130 - } 128 + .collect(GuavaCollectors.toImmutableList());
131 -
132 - return allocations;
133 } 129 }
134 130
135 @Override 131 @Override
...@@ -149,7 +145,7 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent ...@@ -149,7 +145,7 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent
149 Collection<ResourcePath> children = store.getChildResources(parent); 145 Collection<ResourcePath> children = store.getChildResources(parent);
150 return children.stream() 146 return children.stream()
151 // We access store twice in this method, then the store may be updated by others 147 // We access store twice in this method, then the store may be updated by others
152 - .filter(x -> !store.getConsumer(x).isPresent()) 148 + .filter(store::isAvailable)
153 .collect(Collectors.toList()); 149 .collect(Collectors.toList());
154 } 150 }
155 151
...@@ -157,8 +153,7 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent ...@@ -157,8 +153,7 @@ public final class ResourceManager extends AbstractListenerManager<ResourceEvent
157 public boolean isAvailable(ResourcePath resource) { 153 public boolean isAvailable(ResourcePath resource) {
158 checkNotNull(resource); 154 checkNotNull(resource);
159 155
160 - Optional<ResourceConsumer> consumer = store.getConsumer(resource); 156 + return store.isAvailable(resource);
161 - return !consumer.isPresent();
162 } 157 }
163 158
164 @Override 159 @Override
......
...@@ -66,9 +66,10 @@ class MockResourceService implements ResourceService { ...@@ -66,9 +66,10 @@ class MockResourceService implements ResourceService {
66 } 66 }
67 67
68 @Override 68 @Override
69 - public Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource) { 69 + public List<ResourceAllocation> getResourceAllocation(ResourcePath resource) {
70 return Optional.ofNullable(assignment.get(resource)) 70 return Optional.ofNullable(assignment.get(resource))
71 - .map(x -> new ResourceAllocation(resource, x)); 71 + .map(x -> ImmutableList.of(new ResourceAllocation(resource, x)))
72 + .orElse(ImmutableList.of());
72 } 73 }
73 74
74 @Override 75 @Override
......
1 /* 1 /*
2 - * Copyright 2015 Open Networking Laboratory 2 + * Copyright 2015-2016 Open Networking Laboratory
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
...@@ -17,14 +17,18 @@ package org.onosproject.store.newresource.impl; ...@@ -17,14 +17,18 @@ package org.onosproject.store.newresource.impl;
17 17
18 import com.google.common.annotations.Beta; 18 import com.google.common.annotations.Beta;
19 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableList;
20 +import com.google.common.collect.Maps;
20 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Reference; 23 import org.apache.felix.scr.annotations.Reference;
23 import org.apache.felix.scr.annotations.ReferenceCardinality; 24 import org.apache.felix.scr.annotations.ReferenceCardinality;
24 import org.apache.felix.scr.annotations.Service; 25 import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.util.GuavaCollectors;
25 import org.onlab.util.Tools; 27 import org.onlab.util.Tools;
28 +import org.onosproject.net.newresource.ResourceAllocation;
26 import org.onosproject.net.newresource.ResourceConsumer; 29 import org.onosproject.net.newresource.ResourceConsumer;
27 import org.onosproject.net.newresource.ResourceEvent; 30 import org.onosproject.net.newresource.ResourceEvent;
31 +import org.onosproject.net.newresource.ResourceId;
28 import org.onosproject.net.newresource.ResourcePath; 32 import org.onosproject.net.newresource.ResourcePath;
29 import org.onosproject.net.newresource.ResourceStore; 33 import org.onosproject.net.newresource.ResourceStore;
30 import org.onosproject.net.newresource.ResourceStoreDelegate; 34 import org.onosproject.net.newresource.ResourceStoreDelegate;
...@@ -40,7 +44,6 @@ import org.onosproject.store.service.Versioned; ...@@ -40,7 +44,6 @@ import org.onosproject.store.service.Versioned;
40 import org.slf4j.Logger; 44 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory; 45 import org.slf4j.LoggerFactory;
42 46
43 -import java.util.ArrayList;
44 import java.util.Arrays; 47 import java.util.Arrays;
45 import java.util.Collection; 48 import java.util.Collection;
46 import java.util.Collections; 49 import java.util.Collections;
...@@ -49,7 +52,9 @@ import java.util.LinkedHashSet; ...@@ -49,7 +52,9 @@ import java.util.LinkedHashSet;
49 import java.util.List; 52 import java.util.List;
50 import java.util.Map; 53 import java.util.Map;
51 import java.util.Optional; 54 import java.util.Optional;
55 +import java.util.Set;
52 import java.util.stream.Collectors; 56 import java.util.stream.Collectors;
57 +import java.util.stream.Stream;
53 58
54 import static com.google.common.base.Preconditions.checkArgument; 59 import static com.google.common.base.Preconditions.checkArgument;
55 import static com.google.common.base.Preconditions.checkNotNull; 60 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -65,10 +70,12 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -65,10 +70,12 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
65 implements ResourceStore { 70 implements ResourceStore {
66 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class); 71 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
67 72
68 - private static final String CONSUMER_MAP = "onos-resource-consumers"; 73 + private static final String DISCRETE_CONSUMER_MAP = "onos-discrete-consumers";
74 + private static final String CONTINUOUS_CONSUMER_MAP = "onos-continuous-consumers";
69 private static final String CHILD_MAP = "onos-resource-children"; 75 private static final String CHILD_MAP = "onos-resource-children";
70 private static final Serializer SERIALIZER = Serializer.using( 76 private static final Serializer SERIALIZER = Serializer.using(
71 - Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API)); 77 + Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API),
78 + ContinuousResourceAllocation.class);
72 79
73 // TODO: We should provide centralized values for this 80 // TODO: We should provide centralized values for this
74 private static final int MAX_RETRIES = 5; 81 private static final int MAX_RETRIES = 5;
...@@ -77,35 +84,61 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -77,35 +84,61 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected StorageService service; 85 protected StorageService service;
79 86
80 - private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap; 87 + private ConsistentMap<ResourcePath.Discrete, ResourceConsumer> discreteConsumers;
81 - private ConsistentMap<ResourcePath, List<ResourcePath>> childMap; 88 + private ConsistentMap<ResourceId, ContinuousResourceAllocation> continuousConsumers;
89 + private ConsistentMap<ResourcePath.Discrete, Set<ResourcePath>> childMap;
82 90
83 @Activate 91 @Activate
84 public void activate() { 92 public void activate() {
85 - consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder() 93 + discreteConsumers = service.<ResourcePath.Discrete, ResourceConsumer>consistentMapBuilder()
86 - .withName(CONSUMER_MAP) 94 + .withName(DISCRETE_CONSUMER_MAP)
87 .withSerializer(SERIALIZER) 95 .withSerializer(SERIALIZER)
88 .build(); 96 .build();
89 - childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder() 97 + continuousConsumers = service.<ResourceId, ContinuousResourceAllocation>consistentMapBuilder()
98 + .withName(CONTINUOUS_CONSUMER_MAP)
99 + .withSerializer(SERIALIZER)
100 + .build();
101 + childMap = service.<ResourcePath.Discrete, Set<ResourcePath>>consistentMapBuilder()
90 .withName(CHILD_MAP) 102 .withName(CHILD_MAP)
91 .withSerializer(SERIALIZER) 103 .withSerializer(SERIALIZER)
92 .build(); 104 .build();
93 105
94 - Tools.retryable(() -> childMap.put(ResourcePath.ROOT, ImmutableList.of()), 106 + Tools.retryable(() -> childMap.put(ResourcePath.ROOT, new LinkedHashSet<>()),
95 ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY); 107 ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY);
96 log.info("Started"); 108 log.info("Started");
97 } 109 }
98 110
99 @Override 111 @Override
100 - public Optional<ResourceConsumer> getConsumer(ResourcePath resource) { 112 + public List<ResourceConsumer> getConsumers(ResourcePath resource) {
101 checkNotNull(resource); 113 checkNotNull(resource);
114 + checkArgument(resource instanceof ResourcePath.Discrete || resource instanceof ResourcePath.Continuous);
115 +
116 + if (resource instanceof ResourcePath.Discrete) {
117 + return getConsumer((ResourcePath.Discrete) resource);
118 + } else {
119 + return getConsumer((ResourcePath.Continuous) resource);
120 + }
121 + }
102 122
103 - Versioned<ResourceConsumer> consumer = consumerMap.get(resource); 123 + private List<ResourceConsumer> getConsumer(ResourcePath.Discrete resource) {
124 + Versioned<ResourceConsumer> consumer = discreteConsumers.get(resource);
104 if (consumer == null) { 125 if (consumer == null) {
105 - return Optional.empty(); 126 + return ImmutableList.of();
127 + }
128 +
129 + return ImmutableList.of(consumer.value());
130 + }
131 +
132 + private List<ResourceConsumer> getConsumer(ResourcePath.Continuous resource) {
133 + Versioned<ContinuousResourceAllocation> allocations = continuousConsumers.get(resource.id());
134 + if (allocations == null) {
135 + return ImmutableList.of();
106 } 136 }
107 137
108 - return Optional.of(consumer.value()); 138 + return allocations.value().allocations().stream()
139 + .filter(x -> x.resource().equals(resource))
140 + .map(ResourceAllocation::consumer)
141 + .collect(GuavaCollectors.toImmutableList());
109 } 142 }
110 143
111 @Override 144 @Override
...@@ -115,15 +148,16 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -115,15 +148,16 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
115 TransactionContext tx = service.transactionContextBuilder().build(); 148 TransactionContext tx = service.transactionContextBuilder().build();
116 tx.begin(); 149 tx.begin();
117 150
118 - TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap = 151 + TransactionalMap<ResourcePath.Discrete, Set<ResourcePath>> childTxMap =
119 tx.getTransactionalMap(CHILD_MAP, SERIALIZER); 152 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
120 153
121 - Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream() 154 + Map<ResourcePath.Discrete, List<ResourcePath>> resourceMap = resources.stream()
122 .filter(x -> x.parent().isPresent()) 155 .filter(x -> x.parent().isPresent())
123 .collect(Collectors.groupingBy(x -> x.parent().get())); 156 .collect(Collectors.groupingBy(x -> x.parent().get()));
124 157
125 - for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) { 158 + for (Map.Entry<ResourcePath.Discrete, List<ResourcePath>> entry: resourceMap.entrySet()) {
126 - if (!isRegistered(childTxMap, entry.getKey())) { 159 + Optional<ResourcePath.Discrete> child = lookup(childTxMap, entry.getKey());
160 + if (!child.isPresent()) {
127 return abortTransaction(tx); 161 return abortTransaction(tx);
128 } 162 }
129 163
...@@ -150,19 +184,32 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -150,19 +184,32 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
150 TransactionContext tx = service.transactionContextBuilder().build(); 184 TransactionContext tx = service.transactionContextBuilder().build();
151 tx.begin(); 185 tx.begin();
152 186
153 - TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap = 187 + TransactionalMap<ResourcePath.Discrete, Set<ResourcePath>> childTxMap =
154 tx.getTransactionalMap(CHILD_MAP, SERIALIZER); 188 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
155 - TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap = 189 + TransactionalMap<ResourcePath.Discrete, ResourceConsumer> discreteConsumerTxMap =
156 - tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER); 190 + tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER);
191 + TransactionalMap<ResourceId, ContinuousResourceAllocation> continuousConsumerTxMap =
192 + tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER);
157 193
158 - Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream() 194 + // Extract Discrete instances from resources
195 + Map<ResourcePath.Discrete, List<ResourcePath>> resourceMap = resources.stream()
159 .filter(x -> x.parent().isPresent()) 196 .filter(x -> x.parent().isPresent())
160 .collect(Collectors.groupingBy(x -> x.parent().get())); 197 .collect(Collectors.groupingBy(x -> x.parent().get()));
161 198
162 // even if one of the resources is allocated to a consumer, 199 // even if one of the resources is allocated to a consumer,
163 // all unregistrations are regarded as failure 200 // all unregistrations are regarded as failure
164 - for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) { 201 + for (Map.Entry<ResourcePath.Discrete, List<ResourcePath>> entry: resourceMap.entrySet()) {
165 - if (entry.getValue().stream().anyMatch(x -> consumerTxMap.get(x) != null)) { 202 + boolean allocated = entry.getValue().stream().anyMatch(x -> {
203 + if (x instanceof ResourcePath.Discrete) {
204 + return discreteConsumerTxMap.get((ResourcePath.Discrete) x) != null;
205 + } else if (x instanceof ResourcePath.Continuous) {
206 + ContinuousResourceAllocation allocations = continuousConsumerTxMap.get(x.id());
207 + return allocations != null && !allocations.allocations().isEmpty();
208 + } else {
209 + return false;
210 + }
211 + });
212 + if (allocated) {
166 return abortTransaction(tx); 213 return abortTransaction(tx);
167 } 214 }
168 215
...@@ -190,20 +237,40 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -190,20 +237,40 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
190 TransactionContext tx = service.transactionContextBuilder().build(); 237 TransactionContext tx = service.transactionContextBuilder().build();
191 tx.begin(); 238 tx.begin();
192 239
193 - TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap = 240 + TransactionalMap<ResourcePath.Discrete, Set<ResourcePath>> childTxMap =
194 tx.getTransactionalMap(CHILD_MAP, SERIALIZER); 241 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
195 - TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap = 242 + TransactionalMap<ResourcePath.Discrete, ResourceConsumer> discreteConsumerTxMap =
196 - tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER); 243 + tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER);
244 + TransactionalMap<ResourceId, ContinuousResourceAllocation> continuousConsumerTxMap =
245 + tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER);
197 246
198 for (ResourcePath resource: resources) { 247 for (ResourcePath resource: resources) {
199 - if (!isRegistered(childTxMap, resource)) { 248 + if (resource instanceof ResourcePath.Discrete) {
249 + if (!lookup(childTxMap, resource).isPresent()) {
200 return abortTransaction(tx); 250 return abortTransaction(tx);
201 } 251 }
202 252
203 - ResourceConsumer oldValue = consumerTxMap.put(resource, consumer); 253 + ResourceConsumer oldValue = discreteConsumerTxMap.put((ResourcePath.Discrete) resource, consumer);
204 if (oldValue != null) { 254 if (oldValue != null) {
205 return abortTransaction(tx); 255 return abortTransaction(tx);
206 } 256 }
257 + } else if (resource instanceof ResourcePath.Continuous) {
258 + Optional<ResourcePath.Continuous> continuous = lookup(childTxMap, (ResourcePath.Continuous) resource);
259 + if (!continuous.isPresent()) {
260 + return abortTransaction(tx);
261 + }
262 +
263 + ContinuousResourceAllocation allocations = continuousConsumerTxMap.get(continuous.get().id());
264 + if (!hasEnoughResource(continuous.get(), (ResourcePath.Continuous) resource, allocations)) {
265 + return abortTransaction(tx);
266 + }
267 +
268 + boolean success = appendValue(continuousConsumerTxMap,
269 + continuous.get(), new ResourceAllocation(continuous.get(), consumer));
270 + if (!success) {
271 + return abortTransaction(tx);
272 + }
273 + }
207 } 274 }
208 275
209 return tx.commit(); 276 return tx.commit();
...@@ -218,8 +285,10 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -218,8 +285,10 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
218 TransactionContext tx = service.transactionContextBuilder().build(); 285 TransactionContext tx = service.transactionContextBuilder().build();
219 tx.begin(); 286 tx.begin();
220 287
221 - TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap = 288 + TransactionalMap<ResourcePath.Discrete, ResourceConsumer> discreteConsumerTxMap =
222 - tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER); 289 + tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER);
290 + TransactionalMap<ResourceId, ContinuousResourceAllocation> continuousConsumerTxMap =
291 + tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER);
223 Iterator<ResourcePath> resourceIte = resources.iterator(); 292 Iterator<ResourcePath> resourceIte = resources.iterator();
224 Iterator<ResourceConsumer> consumerIte = consumers.iterator(); 293 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
225 294
...@@ -227,33 +296,76 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -227,33 +296,76 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
227 ResourcePath resource = resourceIte.next(); 296 ResourcePath resource = resourceIte.next();
228 ResourceConsumer consumer = consumerIte.next(); 297 ResourceConsumer consumer = consumerIte.next();
229 298
299 + if (resource instanceof ResourcePath.Discrete) {
230 // if this single release fails (because the resource is allocated to another consumer, 300 // if this single release fails (because the resource is allocated to another consumer,
231 // the whole release fails 301 // the whole release fails
232 - if (!consumerTxMap.remove(resource, consumer)) { 302 + if (!discreteConsumerTxMap.remove((ResourcePath.Discrete) resource, consumer)) {
233 return abortTransaction(tx); 303 return abortTransaction(tx);
234 } 304 }
305 + } else if (resource instanceof ResourcePath.Continuous) {
306 + ResourcePath.Continuous continuous = (ResourcePath.Continuous) resource;
307 + ContinuousResourceAllocation allocation = continuousConsumerTxMap.get(continuous.id());
308 + ImmutableList<ResourceAllocation> newAllocations = allocation.allocations().stream()
309 + .filter(x -> !(x.consumer().equals(consumer) &&
310 + ((ResourcePath.Continuous) x.resource()).value() == continuous.value()))
311 + .collect(GuavaCollectors.toImmutableList());
312 +
313 + if (!continuousConsumerTxMap.replace(continuous.id(), allocation,
314 + new ContinuousResourceAllocation(allocation.original(), newAllocations))) {
315 + return abortTransaction(tx);
316 + }
317 + }
235 } 318 }
236 319
237 return tx.commit(); 320 return tx.commit();
238 } 321 }
239 322
240 @Override 323 @Override
324 + public boolean isAvailable(ResourcePath resource) {
325 + checkNotNull(resource);
326 + checkArgument(resource instanceof ResourcePath.Discrete || resource instanceof ResourcePath.Continuous);
327 +
328 + if (resource instanceof ResourcePath.Discrete) {
329 + return getConsumer((ResourcePath.Discrete) resource).isEmpty();
330 + } else {
331 + return isAvailable((ResourcePath.Continuous) resource);
332 + }
333 + }
334 +
335 + private boolean isAvailable(ResourcePath.Continuous resource) {
336 + Versioned<ContinuousResourceAllocation> allocation = continuousConsumers.get(resource.id());
337 + if (allocation == null) {
338 + return false;
339 + }
340 +
341 + return hasEnoughResource(allocation.value().original(), resource, allocation.value());
342 + }
343 +
344 + @Override
241 public Collection<ResourcePath> getResources(ResourceConsumer consumer) { 345 public Collection<ResourcePath> getResources(ResourceConsumer consumer) {
242 checkNotNull(consumer); 346 checkNotNull(consumer);
243 347
244 // NOTE: getting all entries may become performance bottleneck 348 // NOTE: getting all entries may become performance bottleneck
245 // TODO: revisit for better backend data structure 349 // TODO: revisit for better backend data structure
246 - return consumerMap.entrySet().stream() 350 + Stream<ResourcePath.Discrete> discreteStream = discreteConsumers.entrySet().stream()
247 .filter(x -> x.getValue().value().equals(consumer)) 351 .filter(x -> x.getValue().value().equals(consumer))
248 - .map(Map.Entry::getKey) 352 + .map(Map.Entry::getKey);
249 - .collect(Collectors.toList()); 353 +
354 + Stream<ResourcePath.Continuous> continuousStream = continuousConsumers.values().stream()
355 + .flatMap(x -> x.value().allocations().stream()
356 + .map(y -> Maps.immutableEntry(x.value().original(), y)))
357 + .filter(x -> x.getValue().consumer().equals(consumer))
358 + .map(x -> x.getKey());
359 +
360 + return Stream.concat(discreteStream, continuousStream).collect(Collectors.toList());
250 } 361 }
251 362
252 @Override 363 @Override
253 public Collection<ResourcePath> getChildResources(ResourcePath parent) { 364 public Collection<ResourcePath> getChildResources(ResourcePath parent) {
254 checkNotNull(parent); 365 checkNotNull(parent);
366 + checkArgument(parent instanceof ResourcePath.Discrete);
255 367
256 - Versioned<List<ResourcePath>> children = childMap.get(parent); 368 + Versioned<Set<ResourcePath>> children = childMap.get((ResourcePath.Discrete) parent);
257 if (children == null) { 369 if (children == null) {
258 return Collections.emptyList(); 370 return Collections.emptyList();
259 } 371 }
...@@ -265,16 +377,28 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -265,16 +377,28 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
265 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) { 377 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) {
266 checkNotNull(parent); 378 checkNotNull(parent);
267 checkNotNull(cls); 379 checkNotNull(cls);
380 + checkArgument(parent instanceof ResourcePath.Discrete);
268 381
269 - Versioned<List<ResourcePath>> children = childMap.get(parent); 382 + Versioned<Set<ResourcePath>> children = childMap.get((ResourcePath.Discrete) parent);
270 if (children == null) { 383 if (children == null) {
271 return Collections.emptyList(); 384 return Collections.emptyList();
272 } 385 }
273 386
274 - return children.value().stream() 387 + Stream<ResourcePath.Discrete> discrete = children.value().stream()
275 .filter(x -> x.last().getClass().equals(cls)) 388 .filter(x -> x.last().getClass().equals(cls))
276 - .filter(consumerMap::containsKey) 389 + .filter(x -> x instanceof ResourcePath.Discrete)
277 - .collect(Collectors.toList()); 390 + .map(x -> (ResourcePath.Discrete) x)
391 + .filter(discreteConsumers::containsKey);
392 +
393 + Stream<ResourcePath.Continuous> continuous = children.value().stream()
394 + .filter(x -> x.last().getClass().equals(cls))
395 + .filter(x -> x instanceof ResourcePath.Continuous)
396 + .map(x -> (ResourcePath.Continuous) x)
397 + .filter(x -> continuousConsumers.containsKey(x.id()))
398 + .filter(x -> continuousConsumers.get(x.id()) != null)
399 + .filter(x -> !continuousConsumers.get(x.id()).value().allocations().isEmpty());
400 +
401 + return Stream.concat(discrete, continuous).collect(Collectors.toList());
278 } 402 }
279 403
280 /** 404 /**
...@@ -288,6 +412,27 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -288,6 +412,27 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
288 return false; 412 return false;
289 } 413 }
290 414
415 + // Appends the specified ResourceAllocation to the existing values stored in the map
416 + private boolean appendValue(TransactionalMap<ResourceId, ContinuousResourceAllocation> map,
417 + ResourcePath.Continuous original, ResourceAllocation value) {
418 + ContinuousResourceAllocation oldValue = map.putIfAbsent(original.id(),
419 + new ContinuousResourceAllocation(original, ImmutableList.of(value)));
420 + if (oldValue == null) {
421 + return true;
422 + }
423 +
424 + if (oldValue.allocations().contains(value)) {
425 + // don't write to map because all values are already stored
426 + return true;
427 + }
428 +
429 + ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original,
430 + ImmutableList.<ResourceAllocation>builder()
431 + .addAll(oldValue.allocations())
432 + .add(value)
433 + .build());
434 + return map.replace(original.id(), oldValue, newValue);
435 + }
291 /** 436 /**
292 * Appends the values to the existing values associated with the specified key. 437 * Appends the values to the existing values associated with the specified key.
293 * If the map already has all the given values, appending will not happen. 438 * If the map already has all the given values, appending will not happen.
...@@ -299,20 +444,20 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -299,20 +444,20 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
299 * @param <V> type of the element of the list 444 * @param <V> type of the element of the list
300 * @return true if the operation succeeds, false otherwise. 445 * @return true if the operation succeeds, false otherwise.
301 */ 446 */
302 - private <K, V> boolean appendValues(TransactionalMap<K, List<V>> map, K key, List<V> values) { 447 + private <K, V> boolean appendValues(TransactionalMap<K, Set<V>> map, K key, List<V> values) {
303 - List<V> oldValues = map.putIfAbsent(key, new ArrayList<>(values)); 448 + Set<V> oldValues = map.putIfAbsent(key, new LinkedHashSet<>(values));
304 if (oldValues == null) { 449 if (oldValues == null) {
305 return true; 450 return true;
306 } 451 }
307 452
308 - LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues); 453 + if (oldValues.containsAll(values)) {
309 - if (oldSet.containsAll(values)) {
310 // don't write to map because all values are already stored 454 // don't write to map because all values are already stored
311 return true; 455 return true;
312 } 456 }
313 457
314 - oldSet.addAll(values); 458 + LinkedHashSet<V> newValues = new LinkedHashSet<>(oldValues);
315 - return map.replace(key, oldValues, new ArrayList<>(oldSet)); 459 + newValues.addAll(values);
460 + return map.replace(key, oldValues, newValues);
316 } 461 }
317 462
318 /** 463 /**
...@@ -326,37 +471,93 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour ...@@ -326,37 +471,93 @@ public class ConsistentResourceStore extends AbstractStore<ResourceEvent, Resour
326 * @param <V> type of the element of the list 471 * @param <V> type of the element of the list
327 * @return true if the operation succeeds, false otherwise 472 * @return true if the operation succeeds, false otherwise
328 */ 473 */
329 - private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) { 474 + private <K, V> boolean removeValues(TransactionalMap<K, Set<V>> map, K key, List<? extends V> values) {
330 - List<V> oldValues = map.get(key); 475 + Set<V> oldValues = map.putIfAbsent(key, new LinkedHashSet<>());
331 if (oldValues == null) { 476 if (oldValues == null) {
332 - map.put(key, new ArrayList<>());
333 return true; 477 return true;
334 } 478 }
335 479
336 - LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues); 480 + if (values.stream().allMatch(x -> !oldValues.contains(x))) {
337 - if (values.stream().allMatch(x -> !oldSet.contains(x))) {
338 // don't write map because none of the values are stored 481 // don't write map because none of the values are stored
339 return true; 482 return true;
340 } 483 }
341 484
342 - oldSet.removeAll(values); 485 + LinkedHashSet<V> newValues = new LinkedHashSet<>(oldValues);
343 - return map.replace(key, oldValues, new ArrayList<>(oldSet)); 486 + newValues.removeAll(values);
487 + return map.replace(key, oldValues, newValues);
344 } 488 }
345 489
346 /** 490 /**
347 - * Checks if the specified resource is registered as a child of a resource in the map. 491 + * Returns the resource which has the same key as the key of the specified resource
492 + * in the list as a value of the map.
348 * 493 *
349 * @param map map storing parent - child relationship of resources 494 * @param map map storing parent - child relationship of resources
350 - * @param resource resource to be checked 495 + * @param resource resource to be checked for its key
351 - * @return true if the resource is registered, false otherwise. 496 + * @return the resource which is regarded as the same as the specified resource
352 */ 497 */
353 - private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) { 498 + // Naive implementation, which traverses all elements in the list
354 - // root is always regarded to be registered 499 + private <T extends ResourcePath> Optional<T> lookup(
500 + TransactionalMap<ResourcePath.Discrete, Set<ResourcePath>> map, T resource) {
501 + // if it is root, always returns itself
355 if (!resource.parent().isPresent()) { 502 if (!resource.parent().isPresent()) {
356 - return true; 503 + return Optional.of(resource);
357 } 504 }
358 505
359 - List<ResourcePath> value = map.get(resource.parent().get()); 506 + Set<ResourcePath> values = map.get(resource.parent().get());
360 - return value != null && value.contains(resource); 507 + if (values == null) {
508 + return Optional.empty();
509 + }
510 +
511 + @SuppressWarnings("unchecked")
512 + Optional<T> result = values.stream()
513 + .filter(x -> x.id().equals(resource.id()))
514 + .map(x -> (T) x)
515 + .findFirst();
516 + return result;
517 + }
518 +
519 + /**
520 + * Checks if there is enough resource volume to allocated the requested resource
521 + * against the specified resource.
522 + *
523 + * @param original original resource
524 + * @param request requested resource
525 + * @param allocation current allocation of the resource
526 + * @return true if there is enough resource volume. Otherwise, false.
527 + */
528 + private boolean hasEnoughResource(ResourcePath.Continuous original,
529 + ResourcePath.Continuous request,
530 + ContinuousResourceAllocation allocation) {
531 + if (allocation == null) {
532 + return request.value() <= original.value();
533 + }
534 +
535 + double allocated = allocation.allocations().stream()
536 + .filter(x -> x.resource() instanceof ResourcePath.Continuous)
537 + .map(x -> (ResourcePath.Continuous) x.resource())
538 + .mapToDouble(ResourcePath.Continuous::value)
539 + .sum();
540 + double left = original.value() - allocated;
541 + return request.value() <= left;
542 + }
543 +
544 + // internal use only
545 + private static final class ContinuousResourceAllocation {
546 + private final ResourcePath.Continuous original;
547 + private final ImmutableList<ResourceAllocation> allocations;
548 +
549 + private ContinuousResourceAllocation(ResourcePath.Continuous original,
550 + ImmutableList<ResourceAllocation> allocations) {
551 + this.original = original;
552 + this.allocations = allocations;
553 + }
554 +
555 + private ResourcePath.Continuous original() {
556 + return original;
557 + }
558 +
559 + private ImmutableList<ResourceAllocation> allocations() {
560 + return allocations;
561 + }
361 } 562 }
362 } 563 }
......
...@@ -204,6 +204,7 @@ import java.util.Arrays; ...@@ -204,6 +204,7 @@ import java.util.Arrays;
204 import java.util.Collections; 204 import java.util.Collections;
205 import java.util.HashMap; 205 import java.util.HashMap;
206 import java.util.HashSet; 206 import java.util.HashSet;
207 +import java.util.LinkedHashSet;
207 import java.util.LinkedList; 208 import java.util.LinkedList;
208 import java.util.Optional; 209 import java.util.Optional;
209 import java.util.concurrent.ConcurrentHashMap; 210 import java.util.concurrent.ConcurrentHashMap;
...@@ -239,7 +240,8 @@ public final class KryoNamespaces { ...@@ -239,7 +240,8 @@ public final class KryoNamespaces {
239 .register(CopyOnWriteArraySet.class) 240 .register(CopyOnWriteArraySet.class)
240 .register(ArrayList.class, 241 .register(ArrayList.class,
241 LinkedList.class, 242 LinkedList.class,
242 - HashSet.class 243 + HashSet.class,
244 + LinkedHashSet.class
243 ) 245 )
244 .register(Maps.immutableEntry("a", "b").getClass()) 246 .register(Maps.immutableEntry("a", "b").getClass())
245 .register(new ArraysAsListSerializer(), Arrays.asList().getClass()) 247 .register(new ArraysAsListSerializer(), Arrays.asList().getClass())
......