/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.systemservices.impl.resource.util; import com.emc.storageos.systemservices.exceptions.SysClientException; import com.emc.storageos.systemservices.exceptions.SyssvcException; import com.emc.storageos.systemservices.impl.client.SysClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.MediaType; import java.net.URI; import java.util.*; import java.util.concurrent.*; /** * Class that calls an URI on all nodes asynchronously. */ public class NodeDataCollector { private static final Logger _log = LoggerFactory.getLogger(NodeDataCollector.class); private static final int FIXED_THREAD_POOL_SIZE = 256; private static ExecutorService _executorService = Executors.newFixedThreadPool (FIXED_THREAD_POOL_SIZE); private static final int MAX_TASK_WAIT_MIN = 10; public static enum Action { POST, GET; } private static class NodeAPICaller<T> implements Callable<T> { private NodeInfo _nodeInfo; private URI _callURI; private Action _action; private Object _requestObj; private Class<T> _returnType; private String _acceptType; public NodeAPICaller(NodeInfo nodeInfo, URI callURI, Action action, Object requestObj, Class<T> returnType, MediaType acceptType) { _nodeInfo = nodeInfo; _callURI = callURI; _action = action; _requestObj = requestObj; _returnType = returnType; _acceptType = acceptType != null ? acceptType.getType() : null; } public NodeInfo getNodeInfo() { return _nodeInfo; } @Override public T call() throws SysClientException { String baseNodeURL = String.format(SysClientFactory.BASE_URL_FORMAT, _nodeInfo.getIpAddress(), _nodeInfo.getPort()); SysClientFactory.SysClient sysClient = SysClientFactory.getSysClient(URI .create(baseNodeURL)); switch (_action) { case POST: return sysClient.post(_callURI, _returnType, _requestObj); case GET: return sysClient.get(_callURI, _returnType, _acceptType); default: _log.error("Action not supported: {}", _action); throw SyssvcException.syssvcExceptions.sysClientError("Action not supported: " + _action); } } } /** * Calls the passed URI on the list of nodes asynchronously by creating a thread for * each call and returns the results as a map with node id as key. * If node is not reachable or we have exceeded the MAX_TASK_WAIT_MIN time the return map * will not contain the corresponding node results. * * @param nodeInfoList List of nodes on which the URI is called * @param callURI URI to call on the nodes * @param action URI action - GET, POST * @param requestObj request object to send with call. Null if nothing is required. * @param returnType class of the returned object * @param acceptType MediaType for the response. By default it is JSON. * @return Returns a Map<NodeId, returnObj> */ public static <T> Map<String, T> getDataFromNodes(List<NodeInfo> nodeInfoList, String callURI, Action action, Object requestObj, Class<T> returnType, MediaType acceptType) { _log.info("Collecting data from URI {}", callURI); Map<String, Future<T>> futures = new HashMap<String, Future<T>>(); // Submit all tasks, collect future objects by node for (NodeInfo node : nodeInfoList) { NodeAPICaller<T> task = new NodeAPICaller<T>(node, URI.create(callURI), action, requestObj, returnType, acceptType); futures.put(task.getNodeInfo().getId(), _executorService.submit(task)); } // Get results from future objects Map<String, T> nodeDataMap = new HashMap<String, T>(); for (Map.Entry<String, Future<T>> entry : futures.entrySet()) { Future<T> future = entry.getValue(); try { nodeDataMap.put(entry.getKey(), future.get(MAX_TASK_WAIT_MIN, TimeUnit.MINUTES)); } catch (Exception e) { if (future != null && !future.isDone()) { _log.error("Error occurred while getting data from URI {} on node {}: " + e, callURI, entry.getKey()); future.cancel(false); } } } _log.info("Done collecting data for URI {}", callURI); return nodeDataMap; } }