Sho SHIMIZU
Committed by Thomas Vachuska

ONOS-2446: Implement API to declare resource hierarchy

Remove API to define resource boundary
(ResourceAdminService.defineResourceBoundary) to integrate with API for
resource hierarchy

Change-Id: Iffa28dec16320122fe41f4f455000596fa266acb
...@@ -17,7 +17,8 @@ package org.onosproject.net.newresource; ...@@ -17,7 +17,8 @@ package org.onosproject.net.newresource;
17 17
18 import com.google.common.annotations.Beta; 18 import com.google.common.annotations.Beta;
19 19
20 -import java.util.function.Predicate; 20 +import java.util.Arrays;
21 +import java.util.List;
21 22
22 /** 23 /**
23 * Service for administering resource service behavior. 24 * Service for administering resource service behavior.
...@@ -25,13 +26,26 @@ import java.util.function.Predicate; ...@@ -25,13 +26,26 @@ import java.util.function.Predicate;
25 @Beta 26 @Beta
26 public interface ResourceAdminService { 27 public interface ResourceAdminService {
27 /** 28 /**
28 - * Define a boundary of the resource specified by the class. 29 + * Register resources as the children of the parent resource path.
29 - * The specified predicate is expected to return true if the supplied value is
30 - * in the resource boundary and return false if it is out of the boundary.
31 * 30 *
32 - * @param cls class of the resource type 31 + * @param parent parent resource path under which the resource are registered
33 - * @param predicate predicate returning true if the value is in the boundary 32 + * @param children resources to be registered as the children of the parent
34 - * @param <T> type of the resource 33 + * @param <T> type of resources
34 + * @return true if registration is successfully done, false otherwise. Registration
35 + * succeeds when each resource is not registered or unallocated.
35 */ 36 */
36 - <T> void defineResourceBoundary(Class<T> cls, Predicate<T> predicate); 37 + default <T> boolean registerResources(ResourcePath parent, T... children) {
38 + return registerResources(parent, Arrays.asList(children));
39 + }
40 +
41 + /**
42 + * Register resources as the children of the parent resource path.
43 + *
44 + * @param parent parent resource path under which the resource are registered
45 + * @param children resources to be registered as the children of the parent
46 + * @param <T> type of resources
47 + * @return true if registration is successfully done, false otherwise. Registration
48 + * succeeds when each resource is not registered or unallocated.
49 + */
50 + <T> boolean registerResources(ResourcePath parent, List<T> children);
37 } 51 }
......
...@@ -24,7 +24,6 @@ import java.util.List; ...@@ -24,7 +24,6 @@ import java.util.List;
24 import java.util.Objects; 24 import java.util.Objects;
25 import java.util.Optional; 25 import java.util.Optional;
26 26
27 -import static com.google.common.base.Preconditions.checkArgument;
28 import static com.google.common.base.Preconditions.checkNotNull; 27 import static com.google.common.base.Preconditions.checkNotNull;
29 28
30 /** 29 /**
...@@ -42,6 +41,16 @@ public final class ResourcePath { ...@@ -42,6 +41,16 @@ public final class ResourcePath {
42 41
43 private final List<Object> resources; 42 private final List<Object> resources;
44 43
44 + public static final ResourcePath ROOT = new ResourcePath(ImmutableList.of());
45 +
46 + public static ResourcePath child(ResourcePath parent, Object child) {
47 + ImmutableList<Object> components = ImmutableList.builder()
48 + .addAll(parent.components())
49 + .add(child)
50 + .build();
51 + return new ResourcePath(components);
52 + }
53 +
45 /** 54 /**
46 * Creates an resource path from the specified components. 55 * Creates an resource path from the specified components.
47 * 56 *
...@@ -58,7 +67,6 @@ public final class ResourcePath { ...@@ -58,7 +67,6 @@ public final class ResourcePath {
58 */ 67 */
59 public ResourcePath(List<Object> components) { 68 public ResourcePath(List<Object> components) {
60 checkNotNull(components); 69 checkNotNull(components);
61 - checkArgument(components.size() > 0);
62 70
63 this.resources = ImmutableList.copyOf(components); 71 this.resources = ImmutableList.copyOf(components);
64 } 72 }
...@@ -85,7 +93,7 @@ public final class ResourcePath { ...@@ -85,7 +93,7 @@ public final class ResourcePath {
85 * If there is no parent, empty instance will be returned. 93 * If there is no parent, empty instance will be returned.
86 */ 94 */
87 public Optional<ResourcePath> parent() { 95 public Optional<ResourcePath> parent() {
88 - if (resources.size() >= 2) { 96 + if (resources.size() > 0) {
89 return Optional.of(new ResourcePath(resources.subList(0, resources.size() - 1))); 97 return Optional.of(new ResourcePath(resources.subList(0, resources.size() - 1)));
90 } 98 }
91 99
......
...@@ -11,6 +11,19 @@ import java.util.Optional; ...@@ -11,6 +11,19 @@ import java.util.Optional;
11 */ 11 */
12 @Beta 12 @Beta
13 public interface ResourceStore { 13 public interface ResourceStore {
14 +
15 + /**
16 + * Registers the resources as children of the parent resource in transactional way.
17 + * Resource registration is must be done before resource allocation. The state after completion
18 + * of this method is all the resources are registered, or no resource is registered.
19 + * The whole registration fails when any one of the resource can't be registered.
20 + *
21 + * @param parent resource which is the parent of the resource to be registered
22 + * @param children resources to be registered
23 + * @return true if the registration succeeds, false otherwise
24 + */
25 + boolean register(ResourcePath parent, List<ResourcePath> children);
26 +
14 /** 27 /**
15 * Allocates the specified resources to the specified consumer in transactional way. 28 * Allocates the specified resources to the specified consumer in transactional way.
16 * The state after completion of this method is all the resources are allocated to the consumer, 29 * The state after completion of this method is all the resources are allocated to the consumer,
......
...@@ -49,9 +49,11 @@ public class ResourcePathTest { ...@@ -49,9 +49,11 @@ public class ResourcePathTest {
49 .testEquals(); 49 .testEquals();
50 } 50 }
51 51
52 - @Test(expected = IllegalArgumentException.class) 52 + @Test
53 public void testCreateWithZeroComponent() { 53 public void testCreateWithZeroComponent() {
54 ResourcePath path = new ResourcePath(); 54 ResourcePath path = new ResourcePath();
55 +
56 + assertThat(path, is(ResourcePath.ROOT));
55 } 57 }
56 58
57 @Test 59 @Test
...@@ -66,7 +68,7 @@ public class ResourcePathTest { ...@@ -66,7 +68,7 @@ public class ResourcePathTest {
66 public void testNoParent() { 68 public void testNoParent() {
67 ResourcePath path = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1)); 69 ResourcePath path = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1));
68 70
69 - assertThat(path.parent(), is(Optional.empty())); 71 + assertThat(path.parent(), is(Optional.of(ResourcePath.ROOT)));
70 } 72 }
71 73
72 @Test 74 @Test
......
...@@ -17,6 +17,7 @@ package org.onosproject.net.newresource.impl; ...@@ -17,6 +17,7 @@ package org.onosproject.net.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.Lists;
20 import org.apache.felix.scr.annotations.Component; 21 import org.apache.felix.scr.annotations.Component;
21 import org.apache.felix.scr.annotations.Reference; 22 import org.apache.felix.scr.annotations.Reference;
22 import org.apache.felix.scr.annotations.ReferenceCardinality; 23 import org.apache.felix.scr.annotations.ReferenceCardinality;
...@@ -33,9 +34,6 @@ import java.util.Arrays; ...@@ -33,9 +34,6 @@ import java.util.Arrays;
33 import java.util.Collection; 34 import java.util.Collection;
34 import java.util.List; 35 import java.util.List;
35 import java.util.Optional; 36 import java.util.Optional;
36 -import java.util.concurrent.ConcurrentHashMap;
37 -import java.util.concurrent.ConcurrentMap;
38 -import java.util.function.Predicate;
39 import java.util.stream.Collectors; 37 import java.util.stream.Collectors;
40 38
41 import static com.google.common.base.Preconditions.checkNotNull; 39 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -48,8 +46,6 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -48,8 +46,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
48 @Beta 46 @Beta
49 public final class ResourceManager implements ResourceService, ResourceAdminService { 47 public final class ResourceManager implements ResourceService, ResourceAdminService {
50 48
51 - private final ConcurrentMap<Class<?>, Predicate<?>> boundaries = new ConcurrentHashMap<>();
52 -
53 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 49 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
54 protected ResourceStore store; 50 protected ResourceStore store;
55 51
...@@ -79,10 +75,6 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ ...@@ -79,10 +75,6 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ
79 checkNotNull(consumer); 75 checkNotNull(consumer);
80 checkNotNull(resources); 76 checkNotNull(resources);
81 77
82 - if (resources.stream().anyMatch(x -> !isValid(x))) {
83 - return ImmutableList.of();
84 - }
85 -
86 // TODO: implement support of resource hierarchy 78 // TODO: implement support of resource hierarchy
87 // allocation for a particular resource implies allocations for all of the sub-resources need to be done 79 // allocation for a particular resource implies allocations for all of the sub-resources need to be done
88 80
...@@ -177,38 +169,8 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ ...@@ -177,38 +169,8 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ
177 } 169 }
178 170
179 @Override 171 @Override
180 - public <T> void defineResourceBoundary(Class<T> cls, Predicate<T> predicate) { 172 + public <T> boolean registerResources(ResourcePath parent, List<T> children) {
181 - boundaries.put(cls, predicate); 173 + List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
182 - } 174 + return store.register(parent, resources);
183 -
184 - /**
185 - * Returns the predicate associated with the specified resource.
186 - *
187 - * @param resource resource whose associated predicate is to be returned
188 - * @param <T> type of the resource
189 - * @return predicate associated with the resource
190 - * Null if the resource doesn't have an associated predicate.
191 - */
192 - @SuppressWarnings("unchecked")
193 - private <T> Predicate<T> lookupPredicate(T resource) {
194 - return (Predicate<T>) boundaries.get(resource.getClass());
195 - }
196 -
197 - /**
198 - * Returns if the specified resource is in the resource range.
199 - * E.g. VLAN ID against a link must be within 12 bit address space.
200 - *
201 - * @param resource resource to be checked if it is within the resource range
202 - * @return true if the resource within the range, false otherwise
203 - */
204 - boolean isValid(ResourcePath resource) {
205 - List<Object> flatten = resource.components();
206 - Object bottom = flatten.get(flatten.size() - 1);
207 - Predicate<Object> predicate = lookupPredicate(bottom);
208 - if (predicate == null) {
209 - return true;
210 - }
211 -
212 - return predicate.test(bottom);
213 } 175 }
214 } 176 }
......
1 -/*
2 - * Copyright 2015 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.net.newresource.impl;
17 -
18 -import org.junit.Before;
19 -import org.junit.Test;
20 -import org.onlab.packet.VlanId;
21 -import org.onosproject.net.ConnectPoint;
22 -import org.onosproject.net.DeviceId;
23 -import org.onosproject.net.LinkKey;
24 -import org.onosproject.net.PortNumber;
25 -import org.onosproject.net.newresource.ResourcePath;
26 -
27 -import java.util.function.Predicate;
28 -
29 -import static org.hamcrest.Matchers.is;
30 -import static org.junit.Assert.assertThat;
31 -
32 -/**
33 - * Unit tests for ResourceManager.
34 - */
35 -public class ResourceManagerTest {
36 -
37 - private static final DeviceId D1 = DeviceId.deviceId("of:001");
38 - private static final DeviceId D2 = DeviceId.deviceId("of:002");
39 - private static final PortNumber P1 = PortNumber.portNumber(1);
40 - private static final ConnectPoint CP1_1 = new ConnectPoint(D1, P1);
41 - private static final ConnectPoint CP2_1 = new ConnectPoint(D2, P1);
42 - private static final short VLAN_LOWER_LIMIT = 0;
43 - private static final short VLAN_UPPER_LIMIT = 1024;
44 -
45 - private final Predicate<VlanId> vlanPredicate =
46 - x -> x.toShort() >= VLAN_LOWER_LIMIT && x.toShort() < VLAN_UPPER_LIMIT;
47 - private ResourceManager manager;
48 -
49 - @Before
50 - public void setUp() {
51 - manager = new ResourceManager();
52 - }
53 -
54 - /**
55 - * Tests resource boundaries.
56 - */
57 - @Test
58 - public void testBoundary() {
59 - manager.defineResourceBoundary(VlanId.class, vlanPredicate);
60 -
61 - LinkKey linkKey = LinkKey.linkKey(CP1_1, CP2_1);
62 -
63 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId((short) (VLAN_LOWER_LIMIT - 1)))),
64 - is(false));
65 -
66 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId(VLAN_LOWER_LIMIT))),
67 - is(true));
68 -
69 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId((short) 100))),
70 - is(true));
71 -
72 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId((short) (VLAN_UPPER_LIMIT - 1)))),
73 - is(true));
74 -
75 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId(VLAN_UPPER_LIMIT))),
76 - is(false));
77 - }
78 -
79 - /**
80 - * Tests the case that a boundary is not set.
81 - */
82 - @Test
83 - public void testWhenBoundaryNotSet() {
84 - LinkKey linkKey = LinkKey.linkKey(CP1_1, CP2_1);
85 -
86 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId((short) (VLAN_LOWER_LIMIT - 1)))),
87 - is(true));
88 -
89 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId(VLAN_LOWER_LIMIT))),
90 - is(true));
91 -
92 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId((short) 100))),
93 - is(true));
94 -
95 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId((short) (VLAN_UPPER_LIMIT - 1)))),
96 - is(true));
97 -
98 - assertThat(manager.isValid(new ResourcePath(linkKey, VlanId.vlanId(VLAN_UPPER_LIMIT))),
99 - is(true));
100 - }
101 -}
...@@ -35,8 +35,12 @@ import org.onosproject.store.service.Versioned; ...@@ -35,8 +35,12 @@ import org.onosproject.store.service.Versioned;
35 import org.slf4j.Logger; 35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory; 36 import org.slf4j.LoggerFactory;
37 37
38 +import java.util.ArrayList;
39 +import java.util.Arrays;
38 import java.util.Collection; 40 import java.util.Collection;
41 +import java.util.Collections;
39 import java.util.Iterator; 42 import java.util.Iterator;
43 +import java.util.LinkedHashSet;
40 import java.util.List; 44 import java.util.List;
41 import java.util.Map; 45 import java.util.Map;
42 import java.util.Optional; 46 import java.util.Optional;
...@@ -54,18 +58,25 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -54,18 +58,25 @@ import static com.google.common.base.Preconditions.checkNotNull;
54 public class ConsistentResourceStore implements ResourceStore { 58 public class ConsistentResourceStore implements ResourceStore {
55 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class); 59 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
56 60
57 - private static final String MAP_NAME = "onos-resource-consumers"; 61 + private static final String CONSUMER_MAP = "onos-resource-consumers";
58 - private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API); 62 + private static final String CHILD_MAP = "onos-resource-children";
63 + private static final Serializer SERIALIZER = Serializer.using(
64 + Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API));
59 65
60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 protected StorageService service; 67 protected StorageService service;
62 68
63 - private ConsistentMap<ResourcePath, ResourceConsumer> consumers; 69 + private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap;
70 + private ConsistentMap<ResourcePath, List<ResourcePath>> childMap;
64 71
65 @Activate 72 @Activate
66 public void activate() { 73 public void activate() {
67 - consumers = service.<ResourcePath, ResourceConsumer>consistentMapBuilder() 74 + consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
68 - .withName(MAP_NAME) 75 + .withName(CONSUMER_MAP)
76 + .withSerializer(SERIALIZER)
77 + .build();
78 + childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder()
79 + .withName(CHILD_MAP)
69 .withSerializer(SERIALIZER) 80 .withSerializer(SERIALIZER)
70 .build(); 81 .build();
71 } 82 }
...@@ -74,7 +85,7 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -74,7 +85,7 @@ public class ConsistentResourceStore implements ResourceStore {
74 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) { 85 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
75 checkNotNull(resource); 86 checkNotNull(resource);
76 87
77 - Versioned<ResourceConsumer> consumer = consumers.get(resource); 88 + Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
78 if (consumer == null) { 89 if (consumer == null) {
79 return Optional.empty(); 90 return Optional.empty();
80 } 91 }
...@@ -83,6 +94,33 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -83,6 +94,33 @@ public class ConsistentResourceStore implements ResourceStore {
83 } 94 }
84 95
85 @Override 96 @Override
97 + public boolean register(ResourcePath resource, List<ResourcePath> children) {
98 + checkNotNull(resource);
99 + checkNotNull(children);
100 +
101 + TransactionContext tx = service.transactionContextBuilder().build();
102 + tx.begin();
103 +
104 + try {
105 + TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
106 + tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
107 +
108 + if (!isRegistered(childTxMap, resource)) {
109 + return abortTransaction(tx);
110 + }
111 +
112 + if (!appendValue(childTxMap, resource, children)) {
113 + return abortTransaction(tx);
114 + }
115 +
116 + return commitTransaction(tx);
117 + } catch (TransactionException e) {
118 + log.error("Exception thrown, abort the transaction", e);
119 + return abortTransaction(tx);
120 + }
121 + }
122 +
123 + @Override
86 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) { 124 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
87 checkNotNull(resources); 125 checkNotNull(resources);
88 checkNotNull(consumer); 126 checkNotNull(consumer);
...@@ -91,11 +129,18 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -91,11 +129,18 @@ public class ConsistentResourceStore implements ResourceStore {
91 tx.begin(); 129 tx.begin();
92 130
93 try { 131 try {
94 - TransactionalMap<ResourcePath, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER); 132 + TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
133 + tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
134 + TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
135 + tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
136 +
95 for (ResourcePath resource: resources) { 137 for (ResourcePath resource: resources) {
96 - ResourceConsumer existing = txMap.putIfAbsent(resource, consumer); 138 + if (!isRegistered(childTxMap, resource)) {
97 - // if the resource is already allocated to another consumer, the whole allocation fails 139 + return abortTransaction(tx);
98 - if (existing != null) { 140 + }
141 +
142 + ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
143 + if (oldValue != null) {
99 return abortTransaction(tx); 144 return abortTransaction(tx);
100 } 145 }
101 } 146 }
...@@ -117,7 +162,8 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -117,7 +162,8 @@ public class ConsistentResourceStore implements ResourceStore {
117 tx.begin(); 162 tx.begin();
118 163
119 try { 164 try {
120 - TransactionalMap<ResourcePath, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER); 165 + TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
166 + tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
121 Iterator<ResourcePath> resourceIte = resources.iterator(); 167 Iterator<ResourcePath> resourceIte = resources.iterator();
122 Iterator<ResourceConsumer> consumerIte = consumers.iterator(); 168 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
123 169
...@@ -127,7 +173,7 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -127,7 +173,7 @@ public class ConsistentResourceStore implements ResourceStore {
127 173
128 // if this single release fails (because the resource is allocated to another consumer, 174 // if this single release fails (because the resource is allocated to another consumer,
129 // the whole release fails 175 // the whole release fails
130 - if (!txMap.remove(resource, consumer)) { 176 + if (!consumerTxMap.remove(resource, consumer)) {
131 return abortTransaction(tx); 177 return abortTransaction(tx);
132 } 178 }
133 } 179 }
...@@ -145,7 +191,7 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -145,7 +191,7 @@ public class ConsistentResourceStore implements ResourceStore {
145 191
146 // NOTE: getting all entries may become performance bottleneck 192 // NOTE: getting all entries may become performance bottleneck
147 // TODO: revisit for better backend data structure 193 // TODO: revisit for better backend data structure
148 - return consumers.entrySet().stream() 194 + return consumerMap.entrySet().stream()
149 .filter(x -> x.getValue().value().equals(consumer)) 195 .filter(x -> x.getValue().value().equals(consumer))
150 .map(Map.Entry::getKey) 196 .map(Map.Entry::getKey)
151 .collect(Collectors.toList()); 197 .collect(Collectors.toList());
...@@ -156,12 +202,14 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -156,12 +202,14 @@ public class ConsistentResourceStore implements ResourceStore {
156 checkNotNull(parent); 202 checkNotNull(parent);
157 checkNotNull(cls); 203 checkNotNull(cls);
158 204
159 - // NOTE: getting all entries may become performance bottleneck 205 + Versioned<List<ResourcePath>> children = childMap.get(parent);
160 - // TODO: revisit for better backend data structure 206 + if (children == null) {
161 - return consumers.entrySet().stream() 207 + return Collections.emptyList();
162 - .filter(x -> x.getKey().parent().isPresent() && x.getKey().parent().get().equals(parent)) 208 + }
163 - .filter(x -> x.getKey().lastComponent().getClass() == cls) 209 +
164 - .map(Map.Entry::getKey) 210 + return children.value().stream()
211 + .filter(x -> x.lastComponent().getClass().equals(cls))
212 + .filter(consumerMap::containsKey)
165 .collect(Collectors.toList()); 213 .collect(Collectors.toList());
166 } 214 }
167 215
...@@ -186,4 +234,45 @@ public class ConsistentResourceStore implements ResourceStore { ...@@ -186,4 +234,45 @@ public class ConsistentResourceStore implements ResourceStore {
186 tx.commit(); 234 tx.commit();
187 return true; 235 return true;
188 } 236 }
237 +
238 + /**
239 + * Appends the values to the existing values associated with the specified key.
240 + *
241 + * @param map map holding multiple values for a key
242 + * @param key key specifying values
243 + * @param values values to be appended
244 + * @param <K> type of the key
245 + * @param <V> type of the element of the list
246 + * @return true if the operation succeeds, false otherwise.
247 + */
248 + private <K, V> boolean appendValue(TransactionalMap<K, List<V>> map, K key, List<V> values) {
249 + List<V> oldValues = map.get(key);
250 + List<V> newValues;
251 + if (oldValues == null) {
252 + newValues = new ArrayList<>(values);
253 + } else {
254 + LinkedHashSet<V> newSet = new LinkedHashSet<>(oldValues);
255 + newSet.addAll(values);
256 + newValues = new ArrayList<>(newSet);
257 + }
258 +
259 + return map.replace(key, oldValues, newValues);
260 + }
261 +
262 + /**
263 + * Checks if the specified resource is registered as a child of a resource in the map.
264 + *
265 + * @param map map storing parent - child relationship of resources
266 + * @param resource resource to be checked
267 + * @return true if the resource is registered, false otherwise.
268 + */
269 + private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
270 + // root is always regarded to be registered
271 + if (!resource.parent().isPresent()) {
272 + return true;
273 + }
274 +
275 + List<ResourcePath> value = map.get(resource.parent().get());
276 + return value != null && value.contains(resource);
277 + }
189 } 278 }
......