blob: bc1edf1fe6ea64437593575c63da9df2346bd203 [file] [log] [blame]
Brian O'Connor6de2e202015-05-21 14:30:41 -07001package org.onosproject.incubator.store.resource.impl;
jccde3e92e2015-03-28 01:40:44 -07002
3import static org.onlab.util.Tools.groupedThreads;
4import static org.slf4j.LoggerFactory.getLogger;
5
6import java.util.Collection;
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.Iterator;
10import java.util.Map;
11import java.util.Set;
12import java.util.concurrent.ExecutionException;
13import java.util.concurrent.ExecutorService;
14import java.util.concurrent.Executors;
15import java.util.concurrent.Future;
16import java.util.concurrent.TimeUnit;
17import java.util.concurrent.TimeoutException;
jccde3e92e2015-03-28 01:40:44 -070018
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.util.KryoNamespace;
26import org.onosproject.cluster.ClusterService;
Brian O'Connor6de2e202015-05-21 14:30:41 -070027import org.onosproject.incubator.net.resource.label.DefaultLabelResource;
28import org.onosproject.incubator.net.resource.label.LabelResource;
29import org.onosproject.incubator.net.resource.label.LabelResourceDelegate;
30import org.onosproject.incubator.net.resource.label.LabelResourceEvent;
31import org.onosproject.incubator.net.resource.label.LabelResourceEvent.Type;
32import org.onosproject.incubator.net.resource.label.LabelResourceId;
33import org.onosproject.incubator.net.resource.label.LabelResourcePool;
34import org.onosproject.incubator.net.resource.label.LabelResourceRequest;
35import org.onosproject.incubator.net.resource.label.LabelResourceStore;
samuel7a5691a2015-05-23 00:36:32 +080036import org.onosproject.net.Device;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.device.DeviceService;
jccde3e92e2015-03-28 01:40:44 -070039import org.onosproject.store.AbstractStore;
40import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
41import org.onosproject.store.cluster.messaging.ClusterMessage;
42import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
43import org.onosproject.store.flow.ReplicaInfo;
44import org.onosproject.store.flow.ReplicaInfoService;
45import org.onosproject.store.serializers.KryoNamespaces;
jccde3e92e2015-03-28 01:40:44 -070046import org.onosproject.store.service.ConsistentMap;
47import org.onosproject.store.service.Serializer;
48import org.onosproject.store.service.StorageService;
samuel7a5691a2015-05-23 00:36:32 +080049import org.onosproject.store.service.Versioned;
jccde3e92e2015-03-28 01:40:44 -070050import org.slf4j.Logger;
51
52import com.google.common.collect.ImmutableSet;
53import com.google.common.collect.Multimap;
54
55/**
56 * Manages label resources using copycat.
57 */
58@Component(immediate = true, enabled = true)
59@Service
60public class DistributedLabelResourceStore
61 extends AbstractStore<LabelResourceEvent, LabelResourceDelegate>
62 implements LabelResourceStore {
63 private final Logger log = getLogger(getClass());
64
65 private static final String POOL_MAP_NAME = "labelresourcepool";
66
67 private static final String GLOBAL_RESOURCE_POOL_DEVICE_ID = "global_resource_pool_device_id";
jccde3e92e2015-03-28 01:40:44 -070068
69 private ConsistentMap<DeviceId, LabelResourcePool> resourcePool = null;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected StorageService storageService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected ReplicaInfoService replicaInfoManager;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected ClusterCommunicationService clusterCommunicator;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected ClusterService clusterService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DeviceService deviceService;
85
86 private ExecutorService messageHandlingExecutor;
87 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 8;
88 private static final long PEER_REQUEST_TIMEOUT_MS = 5000;
89
samuel7a5691a2015-05-23 00:36:32 +080090 private static final Serializer SERIALIZER = Serializer
91 .using(new KryoNamespace.Builder().register(KryoNamespaces.API)
jccde3e92e2015-03-28 01:40:44 -070092 .register(LabelResourceEvent.class)
93 .register(LabelResourcePool.class).register(DeviceId.class)
94 .register(LabelResourceRequest.class)
95 .register(LabelResourceRequest.Type.class)
96 .register(LabelResourceEvent.Type.class)
97 .register(DefaultLabelResource.class)
samuel7a5691a2015-05-23 00:36:32 +080098 .register(LabelResourceId.class)
99 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID).build());
jccde3e92e2015-03-28 01:40:44 -0700100
101 @Activate
102 public void activate() {
103
104 resourcePool = storageService
105 .<DeviceId, LabelResourcePool>consistentMapBuilder()
samuel7a5691a2015-05-23 00:36:32 +0800106 .withName(POOL_MAP_NAME).withSerializer(SERIALIZER)
107 .withPartitionsDisabled().build();
jccde3e92e2015-03-28 01:40:44 -0700108 messageHandlingExecutor = Executors
109 .newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
110 groupedThreads("onos/store/flow",
111 "message-handlers"));
112 clusterCommunicator
113 .addSubscriber(LabelResourceMessageSubjects.LABEL_POOL_CREATED,
114 new ClusterMessageHandler() {
115
116 @Override
117 public void handle(ClusterMessage message) {
118 LabelResourcePool operation = SERIALIZER
119 .decode(message.payload());
120 log.trace("received get flow entry request for {}",
121 operation);
122 boolean b = internalCreate(operation);
samuel7a5691a2015-05-23 00:36:32 +0800123 message.respond(SERIALIZER.encode(b));
jccde3e92e2015-03-28 01:40:44 -0700124 }
125 }, messageHandlingExecutor);
126 clusterCommunicator
127 .addSubscriber(LabelResourceMessageSubjects.LABEL_POOL_DESTROYED,
128 new ClusterMessageHandler() {
129
130 @Override
131 public void handle(ClusterMessage message) {
132 DeviceId deviceId = SERIALIZER
133 .decode(message.payload());
134 log.trace("received get flow entry request for {}",
135 deviceId);
136 boolean b = internalDestroy(deviceId);
samuel7a5691a2015-05-23 00:36:32 +0800137 message.respond(SERIALIZER.encode(b));
jccde3e92e2015-03-28 01:40:44 -0700138 }
139 }, messageHandlingExecutor);
140 clusterCommunicator
141 .addSubscriber(LabelResourceMessageSubjects.LABEL_POOL_APPLY,
142 new ClusterMessageHandler() {
143
144 @Override
145 public void handle(ClusterMessage message) {
146 LabelResourceRequest request = SERIALIZER
147 .decode(message.payload());
148 log.trace("received get flow entry request for {}",
149 request);
150 final Collection<LabelResource> resource = internalApply(request);
samuel7a5691a2015-05-23 00:36:32 +0800151 message.respond(SERIALIZER
152 .encode(resource));
jccde3e92e2015-03-28 01:40:44 -0700153 }
154 }, messageHandlingExecutor);
155 clusterCommunicator
156 .addSubscriber(LabelResourceMessageSubjects.LABEL_POOL_RELEASE,
157 new ClusterMessageHandler() {
158
159 @Override
160 public void handle(ClusterMessage message) {
161 LabelResourceRequest request = SERIALIZER
162 .decode(message.payload());
163 log.trace("received get flow entry request for {}",
164 request);
165 final boolean isSuccess = internalRelease(request);
samuel7a5691a2015-05-23 00:36:32 +0800166 message.respond(SERIALIZER
167 .encode(isSuccess));
jccde3e92e2015-03-28 01:40:44 -0700168 }
169 }, messageHandlingExecutor);
170 log.info("Started");
171 }
172
173 @Deactivate
174 public void deactivate() {
175 clusterCommunicator
176 .removeSubscriber(LabelResourceMessageSubjects.LABEL_POOL_CREATED);
177 clusterCommunicator
178 .removeSubscriber(LabelResourceMessageSubjects.LABEL_POOL_APPLY);
179 clusterCommunicator
180 .removeSubscriber(LabelResourceMessageSubjects.LABEL_POOL_DESTROYED);
181 clusterCommunicator
182 .removeSubscriber(LabelResourceMessageSubjects.LABEL_POOL_RELEASE);
183 messageHandlingExecutor.shutdown();
184 log.info("Stopped");
185 }
186
187 @Override
188 public boolean createDevicePool(DeviceId deviceId,
189 LabelResourceId beginLabel,
190 LabelResourceId endLabel) {
191 LabelResourcePool pool = new LabelResourcePool(deviceId.toString(),
192 beginLabel.labelId(),
193 endLabel.labelId());
194 return this.create(pool);
195 }
196
197 @Override
198 public boolean createGlobalPool(LabelResourceId beginLabel,
199 LabelResourceId endLabel) {
200 LabelResourcePool pool = new LabelResourcePool(
201 GLOBAL_RESOURCE_POOL_DEVICE_ID,
202 beginLabel.labelId(),
203 endLabel.labelId());
204 return this.internalCreate(pool);
205 }
206
207 private boolean create(LabelResourcePool pool) {
208 Device device = (Device) deviceService.getDevice(pool.deviceId());
209 if (device == null) {
210 return false;
211 }
212
213 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(pool
214 .deviceId());
215
216 if (!replicaInfo.master().isPresent()) {
217 log.warn("Failed to getFlowEntries: No master for {}", pool);
218 return false;
219 }
220
221 if (replicaInfo.master().get()
222 .equals(clusterService.getLocalNode().id())) {
223 return internalCreate(pool);
224 }
225
226 log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
227 replicaInfo.master().orNull(), pool.deviceId());
228
229 return complete(clusterCommunicator
230 .sendAndReceive(pool,
231 LabelResourceMessageSubjects.LABEL_POOL_CREATED,
232 SERIALIZER::encode, SERIALIZER::decode,
233 replicaInfo.master().get()));
234 }
235
236 private boolean internalCreate(LabelResourcePool pool) {
samuel7a5691a2015-05-23 00:36:32 +0800237 Versioned<LabelResourcePool> poolOld = resourcePool
238 .get(pool.deviceId());
jccde3e92e2015-03-28 01:40:44 -0700239 if (poolOld == null) {
240 resourcePool.put(pool.deviceId(), pool);
jccde3e92e2015-03-28 01:40:44 -0700241 LabelResourceEvent event = new LabelResourceEvent(
242 Type.POOL_CREATED,
243 pool);
244 notifyDelegate(event);
245 return true;
246 }
jccde3e92e2015-03-28 01:40:44 -0700247 return false;
248 }
249
250 @Override
251 public boolean destroyDevicePool(DeviceId deviceId) {
252 Device device = (Device) deviceService.getDevice(deviceId);
253 if (device == null) {
254 return false;
255 }
256 ReplicaInfo replicaInfo = replicaInfoManager
257 .getReplicaInfoFor(deviceId);
258
259 if (!replicaInfo.master().isPresent()) {
260 log.warn("Failed to getFlowEntries: No master for {}", deviceId);
261 return false;
262 }
263
264 if (replicaInfo.master().get()
265 .equals(clusterService.getLocalNode().id())) {
266 return internalDestroy(deviceId);
267 }
268
269 log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
270 replicaInfo.master().orNull(), deviceId);
271
272 return complete(clusterCommunicator
273 .sendAndReceive(deviceId,
274 LabelResourceMessageSubjects.LABEL_POOL_DESTROYED,
275 SERIALIZER::encode, SERIALIZER::decode,
276 replicaInfo.master().get()));
277 }
278
279 private boolean internalDestroy(DeviceId deviceId) {
samuel7a5691a2015-05-23 00:36:32 +0800280 Versioned<LabelResourcePool> poolOld = resourcePool.get(deviceId);
jccde3e92e2015-03-28 01:40:44 -0700281 if (poolOld != null) {
282 resourcePool.remove(deviceId);
283 LabelResourceEvent event = new LabelResourceEvent(
284 Type.POOL_CREATED,
samuel7a5691a2015-05-23 00:36:32 +0800285 poolOld.value());
jccde3e92e2015-03-28 01:40:44 -0700286 notifyDelegate(event);
287 }
288 log.info("success to destroy the label resource pool of device id {}",
289 deviceId);
290 return true;
291 }
292
293 @Override
294 public Collection<LabelResource> applyFromDevicePool(DeviceId deviceId,
295 long applyNum) {
296 Device device = (Device) deviceService.getDevice(deviceId);
297 if (device == null) {
298 return Collections.emptyList();
299 }
300 LabelResourceRequest request = new LabelResourceRequest(
301 deviceId,
302 LabelResourceRequest.Type.APPLY,
303 applyNum, null);
304 ReplicaInfo replicaInfo = replicaInfoManager
305 .getReplicaInfoFor(deviceId);
306
307 if (!replicaInfo.master().isPresent()) {
308 log.warn("Failed to getFlowEntries: No master for {}", deviceId);
309 return Collections.emptyList();
310 }
311
312 if (replicaInfo.master().get()
313 .equals(clusterService.getLocalNode().id())) {
314 return internalApply(request);
315 }
316
317 log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
318 replicaInfo.master().orNull(), deviceId);
319
320 return complete(clusterCommunicator
321 .sendAndReceive(request,
322 LabelResourceMessageSubjects.LABEL_POOL_APPLY,
323 SERIALIZER::encode, SERIALIZER::decode,
324 replicaInfo.master().get()));
325 }
326
327 private Collection<LabelResource> internalApply(LabelResourceRequest request) {
jccde3e92e2015-03-28 01:40:44 -0700328 DeviceId deviceId = request.deviceId();
329 long applyNum = request.applyNum();
samuel7a5691a2015-05-23 00:36:32 +0800330 Versioned<LabelResourcePool> poolOld = resourcePool.get(deviceId);
331 LabelResourcePool pool = poolOld.value();
jccde3e92e2015-03-28 01:40:44 -0700332 Collection<LabelResource> result = new HashSet<LabelResource>();
333 long freeNum = this.getFreeNumOfDevicePool(deviceId);
334 if (applyNum > freeNum) {
335 log.info("the free number of the label resource pool of deviceId {} is not enough.");
jccde3e92e2015-03-28 01:40:44 -0700336 return Collections.emptyList();
337 }
338 Set<LabelResource> releaseLabels = new HashSet<LabelResource>(
339 pool.releaseLabelId());
340 long tmp = releaseLabels.size() > applyNum ? applyNum : releaseLabels
341 .size();
342 LabelResource resource = null;
343 for (int i = 0; i < tmp; i++) {
344 Iterator<LabelResource> it = releaseLabels.iterator();
345 if (it.hasNext()) {
346 resource = it.next();
347 releaseLabels.remove(resource);
348 }
349 result.add(resource);
350 }
351 for (long j = pool.currentUsedMaxLabelId().labelId(); j < pool
352 .currentUsedMaxLabelId().labelId() + applyNum - tmp; j++) {
353 resource = new DefaultLabelResource(deviceId,
354 LabelResourceId
355 .labelResourceId(j));
356 result.add(resource);
357 }
358 long beginLabel = pool.beginLabel().labelId();
359 long endLabel = pool.endLabel().labelId();
360 long totalNum = pool.totalNum();
361 long current = pool.currentUsedMaxLabelId().labelId() + applyNum - tmp;
362 long usedNum = pool.usedNum() + applyNum;
363 ImmutableSet<LabelResource> freeLabel = ImmutableSet
364 .copyOf(releaseLabels);
365 LabelResourcePool newPool = new LabelResourcePool(deviceId.toString(),
366 beginLabel, endLabel,
367 totalNum, usedNum,
368 current, freeLabel);
369 resourcePool.put(deviceId, newPool);
370 log.info("success to apply label resource");
jccde3e92e2015-03-28 01:40:44 -0700371 return result;
372 }
373
374 @Override
375 public boolean releaseToDevicePool(Multimap<DeviceId, LabelResource> release) {
376 Map<DeviceId, Collection<LabelResource>> maps = release.asMap();
377 Set<DeviceId> deviceIdSet = maps.keySet();
378 LabelResourceRequest request = null;
379 for (Iterator<DeviceId> it = deviceIdSet.iterator(); it.hasNext();) {
380 DeviceId deviceId = (DeviceId) it.next();
381 Device device = (Device) deviceService.getDevice(deviceId);
382 if (device == null) {
383 continue;
384 }
385 ImmutableSet<LabelResource> collection = ImmutableSet.copyOf(maps
386 .get(deviceId));
387 request = new LabelResourceRequest(
388 deviceId,
389 LabelResourceRequest.Type.RELEASE,
390 0, collection);
391 ReplicaInfo replicaInfo = replicaInfoManager
392 .getReplicaInfoFor(deviceId);
393
394 if (!replicaInfo.master().isPresent()) {
395 log.warn("Failed to getFlowEntries: No master for {}", deviceId);
396 return false;
397 }
398
399 if (replicaInfo.master().get()
400 .equals(clusterService.getLocalNode().id())) {
401 return internalRelease(request);
402 }
403
404 log.trace("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
405 replicaInfo.master().orNull(), deviceId);
406
407 return complete(clusterCommunicator
408 .sendAndReceive(request,
409 LabelResourceMessageSubjects.LABEL_POOL_RELEASE,
410 SERIALIZER::encode, SERIALIZER::decode,
411 replicaInfo.master().get()));
412 }
413 return false;
414 }
415
416 private boolean internalRelease(LabelResourceRequest request) {
jccde3e92e2015-03-28 01:40:44 -0700417 DeviceId deviceId = request.deviceId();
418 Collection<LabelResource> release = request.releaseCollection();
samuel7a5691a2015-05-23 00:36:32 +0800419 Versioned<LabelResourcePool> poolOld = resourcePool.get(deviceId);
420 LabelResourcePool pool = poolOld.value();
jccde3e92e2015-03-28 01:40:44 -0700421 if (pool == null) {
jccde3e92e2015-03-28 01:40:44 -0700422 log.info("the label resource pool of device id {} does not exist");
423 return false;
424 }
425 Set<LabelResource> storeSet = new HashSet<LabelResource>(
426 pool.releaseLabelId());
427 LabelResource labelResource = null;
428 long realReleasedNum = 0;
429 for (Iterator<LabelResource> it = release.iterator(); it.hasNext();) {
430 labelResource = it.next();
431 if (labelResource.labelResourceId().labelId() < pool.beginLabel()
432 .labelId()
433 || labelResource.labelResourceId().labelId() > pool
434 .endLabel().labelId()) {
435 continue;
436 }
437 if (pool.currentUsedMaxLabelId().labelId() > labelResource
438 .labelResourceId().labelId()
439 || !storeSet.contains(labelResource)) {
440 storeSet.add(labelResource);
441 realReleasedNum++;
442 }
443 }
444 long beginNum = pool.beginLabel().labelId();
445 long endNum = pool.endLabel().labelId();
446 long totalNum = pool.totalNum();
447 long usedNum = pool.usedNum() - realReleasedNum;
448 long current = pool.currentUsedMaxLabelId().labelId();
449 ImmutableSet<LabelResource> s = ImmutableSet.copyOf(storeSet);
450 LabelResourcePool newPool = new LabelResourcePool(deviceId.toString(),
451 beginNum, endNum,
452 totalNum, usedNum,
453 current, s);
454 resourcePool.put(deviceId, newPool);
455 log.info("success to release label resource");
jccde3e92e2015-03-28 01:40:44 -0700456 return true;
457 }
458
459 @Override
460 public boolean isDevicePoolFull(DeviceId deviceId) {
samuel7a5691a2015-05-23 00:36:32 +0800461 Versioned<LabelResourcePool> pool = resourcePool.get(deviceId);
jccde3e92e2015-03-28 01:40:44 -0700462 if (pool == null) {
463 return true;
464 }
samuel7a5691a2015-05-23 00:36:32 +0800465 return pool.value().currentUsedMaxLabelId() == pool.value().endLabel()
466 && pool.value().releaseLabelId().size() == 0 ? true : false;
jccde3e92e2015-03-28 01:40:44 -0700467 }
468
469 @Override
470 public long getFreeNumOfDevicePool(DeviceId deviceId) {
samuel7a5691a2015-05-23 00:36:32 +0800471 Versioned<LabelResourcePool> pool = resourcePool.get(deviceId);
jccde3e92e2015-03-28 01:40:44 -0700472 if (pool == null) {
473 return 0;
474 }
samuel7a5691a2015-05-23 00:36:32 +0800475 return pool.value().endLabel().labelId()
476 - pool.value().currentUsedMaxLabelId().labelId()
477 + pool.value().releaseLabelId().size();
jccde3e92e2015-03-28 01:40:44 -0700478 }
479
480 @Override
481 public LabelResourcePool getDeviceLabelResourcePool(DeviceId deviceId) {
samuel7a5691a2015-05-23 00:36:32 +0800482 Versioned<LabelResourcePool> pool = resourcePool.get(deviceId);
483 return pool == null ? null : pool.value();
jccde3e92e2015-03-28 01:40:44 -0700484 }
485
486 @Override
487 public boolean destroyGlobalPool() {
488 return this.internalDestroy(DeviceId
489 .deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID));
490 }
491
492 @Override
493 public Collection<LabelResource> applyFromGlobalPool(long applyNum) {
494 LabelResourceRequest request = new LabelResourceRequest(
495 DeviceId.deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID),
496 LabelResourceRequest.Type.APPLY,
497 applyNum, null);
498 return this.internalApply(request);
499 }
500
501 @Override
502 public boolean releaseToGlobalPool(Set<LabelResourceId> release) {
503 Set<LabelResource> set = new HashSet<LabelResource>();
504 DefaultLabelResource resource = null;
505 for (LabelResourceId labelResource : release) {
506 resource = new DefaultLabelResource(
507 DeviceId.deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID),
508 labelResource);
509 set.add(resource);
510 }
511 LabelResourceRequest request = new LabelResourceRequest(
512 DeviceId.deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID),
513 LabelResourceRequest.Type.APPLY,
514 0,
515 ImmutableSet
516 .copyOf(set));
517 return this.internalRelease(request);
518 }
519
520 @Override
521 public boolean isGlobalPoolFull() {
522 return this.isDevicePoolFull(DeviceId
523 .deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID));
524 }
525
526 @Override
527 public long getFreeNumOfGlobalPool() {
528 return this.getFreeNumOfDevicePool(DeviceId
529 .deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID));
530 }
531
532 @Override
533 public LabelResourcePool getGlobalLabelResourcePool() {
534 return this.getDeviceLabelResourcePool(DeviceId
535 .deviceId(GLOBAL_RESOURCE_POOL_DEVICE_ID));
536 }
537
538 private <T> T complete(Future<T> future) {
539 try {
samuel7a5691a2015-05-23 00:36:32 +0800540 return future.get(PEER_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
jccde3e92e2015-03-28 01:40:44 -0700541 } catch (InterruptedException e) {
542 Thread.currentThread().interrupt();
543 log.error("Interrupted while waiting for operation to complete.", e);
544 return null;
545 } catch (TimeoutException | ExecutionException e) {
546 log.error("Failed remote operation", e);
547 return null;
548 }
549 }
550}