/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.processors.rest.handlers.top;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cluster.ClusterMetrics;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.client.GridClientCacheMode;
import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
import org.apache.ignite.internal.processors.port.GridPortRecord;
import org.apache.ignite.internal.processors.rest.GridRestCommand;
import org.apache.ignite.internal.processors.rest.GridRestProtocol;
import org.apache.ignite.internal.processors.rest.GridRestResponse;
import org.apache.ignite.internal.processors.rest.client.message.GridClientNodeBean;
import org.apache.ignite.internal.processors.rest.client.message.GridClientCacheBean;
import org.apache.ignite.internal.processors.rest.client.message.GridClientNodeMetricsBean;
import org.apache.ignite.internal.processors.rest.handlers.GridRestCommandHandlerAdapter;
import org.apache.ignite.internal.processors.rest.request.GridRestRequest;
import org.apache.ignite.internal.processors.rest.request.GridRestTopologyRequest;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.P1;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.IgnitePortProtocol;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BINARY_CONFIGURATION;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_CACHE;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_NODE_CONSISTENT_ID;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_REST_TCP_ADDRS;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_REST_TCP_HOST_NAMES;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_REST_TCP_PORT;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_SECURITY_SUBJECT;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_TX_CONFIG;
import static org.apache.ignite.internal.processors.rest.GridRestCommand.NODE;
import static org.apache.ignite.internal.processors.rest.GridRestCommand.TOPOLOGY;
/**
* Command handler for API requests.
*/
public class GridTopologyCommandHandler extends GridRestCommandHandlerAdapter {
/** Supported commands. */
private static final Collection<GridRestCommand> SUPPORTED_COMMANDS = U.sealList(TOPOLOGY, NODE);
/**
* @param ctx Context.
*/
public GridTopologyCommandHandler(GridKernalContext ctx) {
super(ctx);
}
/** {@inheritDoc} */
@Override public Collection<GridRestCommand> supportedCommands() {
return SUPPORTED_COMMANDS;
}
/** {@inheritDoc} */
@Override public IgniteInternalFuture<GridRestResponse> handleAsync(GridRestRequest req) {
assert req instanceof GridRestTopologyRequest : "Invalid command for topology handler: " + req;
assert SUPPORTED_COMMANDS.contains(req.command());
if (log.isDebugEnabled())
log.debug("Handling topology REST request: " + req);
GridRestTopologyRequest req0 = (GridRestTopologyRequest)req;
GridRestResponse res = new GridRestResponse();
boolean mtr = req0.includeMetrics();
boolean attr = req0.includeAttributes();
switch (req.command()) {
case TOPOLOGY: {
Collection<ClusterNode> allNodes = F.concat(false,
ctx.discovery().allNodes(), ctx.discovery().daemonNodes());
Collection<GridClientNodeBean> top =
new ArrayList<>(allNodes.size());
for (ClusterNode node : allNodes)
top.add(createNodeBean(node, mtr, attr));
res.setResponse(top);
break;
}
case NODE: {
UUID id = req0.nodeId();
final String ip = req0.nodeIp();
if (id == null && ip == null)
return new GridFinishedFuture<>(new IgniteCheckedException(
"Failed to handle request (either id or ip should be specified)."));
ClusterNode node;
if (id != null) {
// Always refresh topology so client see most up-to-date view.
ctx.discovery().alive(id);
node = ctx.grid().cluster().node(id);
if (ip != null && node != null && !containsIp(node.addresses(), ip))
node = null;
}
else
node = F.find(ctx.discovery().allNodes(), null, new P1<ClusterNode>() {
@Override
public boolean apply(ClusterNode n) {
return containsIp(n.addresses(), ip);
}
});
if (node != null)
res.setResponse(createNodeBean(node, mtr, attr));
else
res.setResponse(null);
break;
}
default:
assert false : "Invalid command for topology handler: " + req;
}
if (log.isDebugEnabled())
log.debug("Handled topology REST request [res=" + res + ", req=" + req + ']');
return new GridFinishedFuture<>(res);
}
/**
* @param addrs List of string addresses.
* @param ip Ip to match.
* @return Whether {@code ip} present in addresses.
*/
private boolean containsIp(Iterable<String> addrs, String ip) {
for (String addr : addrs) {
try {
if (InetAddress.getByName(addr).getHostAddress().equals(ip))
return true;
}
catch (UnknownHostException ignored) {
// It's ok if we just don't know that host - node could be bound to address in another network.
}
}
return false;
}
/**
* Creates cache bean.
*
* @param ccfg Cache configuration.
* @return Cache bean.
*/
public GridClientCacheBean createCacheBean(CacheConfiguration ccfg) {
GridClientCacheBean cacheBean = new GridClientCacheBean();
cacheBean.setName(ccfg.getName());
cacheBean.setMode(GridClientCacheMode.valueOf(ccfg.getCacheMode().toString()));
cacheBean.setSqlSchema(ccfg.getSqlSchema());
return cacheBean;
}
/**
* Creates node bean out of grid node. Notice that cache attribute is handled separately.
*
* @param node Grid node.
* @param mtr {@code true} to add metrics.
* @param attr {@code true} to add attributes.
* @return Grid Node bean.
*/
private GridClientNodeBean createNodeBean(ClusterNode node, boolean mtr, boolean attr) {
assert node != null;
GridClientNodeBean nodeBean = new GridClientNodeBean();
nodeBean.setNodeId(node.id());
nodeBean.setConsistentId(node.consistentId());
nodeBean.setTcpPort(attribute(node, ATTR_REST_TCP_PORT, 0));
nodeBean.setTcpAddresses(nonEmptyList(node.<Collection<String>>attribute(ATTR_REST_TCP_ADDRS)));
nodeBean.setTcpHostNames(nonEmptyList(node.<Collection<String>>attribute(ATTR_REST_TCP_HOST_NAMES)));
GridCacheProcessor cacheProc = ctx.cache();
Map<String, CacheMode> nodeCaches = ctx.discovery().nodeCaches(node);
Collection<GridClientCacheBean> caches = new ArrayList<>(nodeCaches.size());
for (String cacheName : nodeCaches.keySet())
caches.add(createCacheBean(cacheProc.cacheConfiguration(cacheName)));
nodeBean.setCaches(caches);
if (mtr) {
ClusterMetrics metrics = node.metrics();
GridClientNodeMetricsBean metricsBean = new GridClientNodeMetricsBean();
metricsBean.setStartTime(metrics.getStartTime());
metricsBean.setAverageActiveJobs(metrics.getAverageActiveJobs());
metricsBean.setAverageCancelledJobs(metrics.getAverageCancelledJobs());
metricsBean.setAverageCpuLoad(metrics.getAverageCpuLoad());
metricsBean.setAverageJobExecuteTime(metrics.getAverageJobExecuteTime());
metricsBean.setAverageJobWaitTime(metrics.getAverageJobWaitTime());
metricsBean.setAverageRejectedJobs(metrics.getAverageRejectedJobs());
metricsBean.setAverageWaitingJobs(metrics.getAverageWaitingJobs());
metricsBean.setCurrentActiveJobs(metrics.getCurrentActiveJobs());
metricsBean.setCurrentCancelledJobs(metrics.getCurrentCancelledJobs());
metricsBean.setCurrentCpuLoad(metrics.getCurrentCpuLoad());
metricsBean.setCurrentGcCpuLoad(metrics.getCurrentGcCpuLoad());
metricsBean.setCurrentDaemonThreadCount(metrics.getCurrentDaemonThreadCount());
metricsBean.setCurrentIdleTime(metrics.getCurrentIdleTime());
metricsBean.setCurrentJobExecuteTime(metrics.getCurrentJobExecuteTime());
metricsBean.setCurrentJobWaitTime(metrics.getCurrentJobWaitTime());
metricsBean.setCurrentRejectedJobs(metrics.getCurrentRejectedJobs());
metricsBean.setCurrentThreadCount(metrics.getCurrentThreadCount());
metricsBean.setCurrentWaitingJobs(metrics.getCurrentWaitingJobs());
metricsBean.setHeapMemoryCommitted(metrics.getHeapMemoryCommitted());
metricsBean.setHeapMemoryInitialized(metrics.getHeapMemoryInitialized());
metricsBean.setHeapMemoryMaximum(metrics.getHeapMemoryMaximum());
metricsBean.setHeapMemoryUsed(metrics.getHeapMemoryUsed());
metricsBean.setLastDataVersion(metrics.getLastDataVersion());
metricsBean.setLastUpdateTime(metrics.getLastUpdateTime());
metricsBean.setMaximumActiveJobs(metrics.getMaximumActiveJobs());
metricsBean.setMaximumCancelledJobs(metrics.getMaximumCancelledJobs());
metricsBean.setMaximumJobExecuteTime(metrics.getMaximumJobExecuteTime());
metricsBean.setMaximumJobWaitTime(metrics.getMaximumJobWaitTime());
metricsBean.setMaximumRejectedJobs(metrics.getMaximumRejectedJobs());
metricsBean.setMaximumThreadCount(metrics.getMaximumThreadCount());
metricsBean.setMaximumWaitingJobs(metrics.getMaximumWaitingJobs());
metricsBean.setNodeStartTime(metrics.getNodeStartTime());
metricsBean.setNonHeapMemoryCommitted(metrics.getNonHeapMemoryCommitted());
metricsBean.setNonHeapMemoryInitialized(metrics.getNonHeapMemoryInitialized());
metricsBean.setNonHeapMemoryMaximum(metrics.getNonHeapMemoryMaximum());
metricsBean.setNonHeapMemoryUsed(metrics.getNonHeapMemoryUsed());
metricsBean.setStartTime(metrics.getStartTime());
metricsBean.setTotalCancelledJobs(metrics.getTotalCancelledJobs());
metricsBean.setTotalCpus(metrics.getTotalCpus());
metricsBean.setTotalExecutedJobs(metrics.getTotalExecutedJobs());
metricsBean.setTotalIdleTime(metrics.getTotalIdleTime());
metricsBean.setTotalRejectedJobs(metrics.getTotalRejectedJobs());
metricsBean.setTotalStartedThreadCount(metrics.getTotalStartedThreadCount());
metricsBean.setTotalExecutedTasks(metrics.getTotalExecutedTasks());
metricsBean.setSentMessagesCount(metrics.getSentMessagesCount());
metricsBean.setSentBytesCount(metrics.getSentBytesCount());
metricsBean.setReceivedMessagesCount(metrics.getReceivedMessagesCount());
metricsBean.setReceivedBytesCount(metrics.getReceivedBytesCount());
metricsBean.setUpTime(metrics.getUpTime());
nodeBean.setMetrics(metricsBean);
}
if (attr) {
Map<String, Object> attrs = new HashMap<>(node.attributes());
attrs.remove(ATTR_CACHE);
attrs.remove(ATTR_TX_CONFIG);
attrs.remove(ATTR_SECURITY_SUBJECT);
attrs.remove(ATTR_SECURITY_CREDENTIALS);
attrs.remove(ATTR_BINARY_CONFIGURATION);
attrs.remove(ATTR_NODE_CONSISTENT_ID);
for (Iterator<Map.Entry<String, Object>> i = attrs.entrySet().iterator(); i.hasNext();) {
Map.Entry<String, Object> e = i.next();
if (!e.getKey().startsWith("org.apache.ignite.") && !e.getKey().startsWith("plugins.") &&
System.getProperty(e.getKey()) == null) {
i.remove();
continue;
}
if (e.getValue() != null) {
if (e.getValue().getClass().isEnum() || e.getValue() instanceof InetAddress)
e.setValue(e.getValue().toString());
else if (e.getValue().getClass().isArray())
i.remove();
}
}
nodeBean.setAttributes(attrs);
}
return nodeBean;
}
/**
* @param col Collection;
* @return Non-empty list.
*/
private static Collection<String> nonEmptyList(Collection<String> col) {
return col == null ? Collections.<String>emptyList() : col;
}
/**
* Get node attribute by specified attribute name.
*
* @param node Node to get attribute for.
* @param attrName Attribute name.
* @param dfltVal Default result for case when node attribute resolved into {@code null}.
* @return Attribute value or default result if requested attribute resolved into {@code null}.
*/
private <T> T attribute(ClusterNode node, String attrName, T dfltVal) {
T attr = node.attribute(attrName);
return attr == null ? dfltVal : attr;
}
/**
* Get registered port
*
* @param protoCls Protocol class.
* @param def Default value if such class is not registered.
* @return Registered port for the protocol class or {@code def}ault value if such class is not registered.
*/
private int getRegisteredPort(Class<? extends GridRestProtocol> protoCls, int def) {
for (GridPortRecord r : ctx.ports().records()) {
if (r.protocol() == IgnitePortProtocol.TCP && protoCls.isAssignableFrom(r.clazz()))
return r.port();
}
return def;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridTopologyCommandHandler.class, this);
}
}