/* * 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.spi.discovery.tcp.internal; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.spi.discovery.DiscoveryDataBag; import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType.CONTINUOUS_PROC; /** * Carries discovery data in marshalled form * and allows convenient way of converting it to and from {@link DiscoveryDataBag} objects. */ public class DiscoveryDataPacket implements Serializable { /** */ private static final long serialVersionUID = 0L; /** */ private final UUID joiningNodeId; /** */ private Map<Integer, byte[]> joiningNodeData = new HashMap<>(); /** */ private Map<Integer, byte[]> commonData = new HashMap<>(); /** */ private Map<UUID, Map<Integer, byte[]>> nodeSpecificData = new LinkedHashMap<>(); /** * @param joiningNodeId Joining node id. */ public DiscoveryDataPacket(UUID joiningNodeId) { this.joiningNodeId = joiningNodeId; } /** * */ public UUID joiningNodeId() { return joiningNodeId; } /** * @param bag Bag. * @param nodeId Node id. * @param marsh Marsh. * @param log Logger. */ public void marshalGridNodeData(DiscoveryDataBag bag, UUID nodeId, Marshaller marsh, IgniteLogger log) { marshalData(bag.commonData(), commonData, marsh, log); Map<Integer, Serializable> locNodeSpecificData = bag.localNodeSpecificData(); if (locNodeSpecificData != null) { Map<Integer, byte[]> marshLocNodeSpecificData = U.newHashMap(locNodeSpecificData.size()); marshalData(locNodeSpecificData, marshLocNodeSpecificData, marsh, log); filterDuplicatedData(marshLocNodeSpecificData); if (!marshLocNodeSpecificData.isEmpty()) nodeSpecificData.put(nodeId, marshLocNodeSpecificData); } } /** * @param bag Bag. * @param marsh Marsh. * @param log Logger. */ public void marshalJoiningNodeData(DiscoveryDataBag bag, Marshaller marsh, IgniteLogger log) { marshalData(bag.joiningNodeData(), joiningNodeData, marsh, log); } /** * @param marsh Marsh. * @param clsLdr Class loader. * @param clientNode Client node. * @param log Logger. */ public DiscoveryDataBag unmarshalGridData( Marshaller marsh, ClassLoader clsLdr, boolean clientNode, IgniteLogger log ) { DiscoveryDataBag dataBag = new DiscoveryDataBag(joiningNodeId); if (commonData != null && !commonData.isEmpty()) { Map<Integer, Serializable> unmarshCommonData = unmarshalData(commonData, marsh, clsLdr, clientNode, log); dataBag.commonData(unmarshCommonData); } if (nodeSpecificData != null && !nodeSpecificData.isEmpty()) { Map<UUID, Map<Integer, Serializable>> unmarshNodeSpecData = U.newLinkedHashMap(nodeSpecificData.size()); for (Map.Entry<UUID, Map<Integer, byte[]>> nodeBinEntry : nodeSpecificData.entrySet()) { Map<Integer, byte[]> nodeBinData = nodeBinEntry.getValue(); if (nodeBinData == null || nodeBinData.isEmpty()) continue; Map<Integer, Serializable> unmarshData = unmarshalData(nodeBinData, marsh, clsLdr, clientNode, log); unmarshNodeSpecData.put(nodeBinEntry.getKey(), unmarshData); } dataBag.nodeSpecificData(unmarshNodeSpecData); } return dataBag; } /** * @param marsh Marsh. * @param clsLdr Class loader. * @param clientNode Client node. * @param log Logger. */ public DiscoveryDataBag unmarshalJoiningNodeData( Marshaller marsh, ClassLoader clsLdr, boolean clientNode, IgniteLogger log ) { DiscoveryDataBag dataBag = new DiscoveryDataBag(joiningNodeId); if (joiningNodeData != null && !joiningNodeData.isEmpty()) { Map<Integer, Serializable> unmarshJoiningNodeData = unmarshalData( joiningNodeData, marsh, clsLdr, clientNode, log); dataBag.joiningNodeData(unmarshJoiningNodeData); } return dataBag; } /** * */ public boolean hasJoiningNodeData() { return joiningNodeData != null && !joiningNodeData.isEmpty(); } /** * @param nodeId Node id. */ public boolean hasDataFromNode(UUID nodeId) { return nodeSpecificData.containsKey(nodeId); } /** * @param existingDataPacket Existing data packet. * @param mrgdCmnDataKeys Mrgd cmn data keys. * @param mrgdSpecifDataKeys Mrgd specif data keys. */ public boolean mergeDataFrom( DiscoveryDataPacket existingDataPacket, Collection<Integer> mrgdCmnDataKeys, Collection<UUID> mrgdSpecifDataKeys ) { if (commonData.size() != mrgdCmnDataKeys.size()) { for (Map.Entry<Integer, byte[]> e : commonData.entrySet()) { if (!mrgdCmnDataKeys.contains(e.getKey())) { byte[] data = existingDataPacket.commonData.get(e.getKey()); if (data != null && Arrays.equals(e.getValue(), data)) { e.setValue(data); boolean add = mrgdCmnDataKeys.add(e.getKey()); assert add; if (mrgdCmnDataKeys.size() == commonData.size()) break; } } } } if (nodeSpecificData.size() != mrgdSpecifDataKeys.size()) { for (Map.Entry<UUID, Map<Integer, byte[]>> e : nodeSpecificData.entrySet()) { if (!mrgdSpecifDataKeys.contains(e.getKey())) { Map<Integer, byte[]> data = existingDataPacket.nodeSpecificData.get(e.getKey()); if (data != null && mapsEqual(e.getValue(), data)) { e.setValue(data); boolean add = mrgdSpecifDataKeys.add(e.getKey()); assert add; if (mrgdSpecifDataKeys.size() == nodeSpecificData.size()) break; } } } } return (mrgdCmnDataKeys.size() == commonData.size()) && (mrgdSpecifDataKeys.size() == nodeSpecificData.size()); } /** * @param m1 first map to compare. * @param m2 second map to compare. */ private boolean mapsEqual(Map<Integer, byte[]> m1, Map<Integer, byte[]> m2) { if (m1 == m2) return true; if (m1.size() == m2.size()) { for (Map.Entry<Integer, byte[]> e : m1.entrySet()) { byte[] data = m2.get(e.getKey()); if (!Arrays.equals(e.getValue(), data)) return false; } return true; } return false; } /** * @param src Source. * @param marsh Marsh. * @param clsLdr Class loader. * @param log Logger. */ private Map<Integer, Serializable> unmarshalData( Map<Integer, byte[]> src, Marshaller marsh, ClassLoader clsLdr, boolean clientNode, IgniteLogger log ) { Map<Integer, Serializable> res = U.newHashMap(src.size()); for (Map.Entry<Integer, byte[]> binEntry : src.entrySet()) { try { Serializable compData = marsh.unmarshal(binEntry.getValue(), clsLdr); res.put(binEntry.getKey(), compData); } catch (IgniteCheckedException e) { if (CONTINUOUS_PROC.ordinal() == binEntry.getKey() && X.hasCause(e, ClassNotFoundException.class) && clientNode) U.warn(log, "Failed to unmarshal continuous query remote filter on client node. Can be ignored."); else U.error(log, "Failed to unmarshal discovery data for component: " + binEntry.getKey(), e); } } return res; } /** * @param src Source. * @param target Target. * @param marsh Marsh. * @param log Logger. */ private void marshalData( Map<Integer, Serializable> src, Map<Integer, byte[]> target, Marshaller marsh, IgniteLogger log ) { //may happen if nothing was collected from components, // corresponding map (for common data or for node specific data) left null if (src == null) return; for (Map.Entry<Integer, Serializable> entry : src.entrySet()) { try { target.put(entry.getKey(), marsh.marshal(entry.getValue())); } catch (IgniteCheckedException e) { U.error(log, "Failed to marshal discovery data " + "[comp=" + entry.getKey() + ", data=" + entry.getValue() + ']', e); } } } /** * TODO https://issues.apache.org/jira/browse/IGNITE-4435 */ private void filterDuplicatedData(Map<Integer, byte[]> discoData) { for (Map<Integer, byte[]> existingData : nodeSpecificData.values()) { Iterator<Map.Entry<Integer, byte[]>> it = discoData.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, byte[]> discoDataEntry = it.next(); byte[] curData = existingData.get(discoDataEntry.getKey()); if (Arrays.equals(curData, discoDataEntry.getValue())) it.remove(); } if (discoData.isEmpty()) break; } } /** * Returns {@link DiscoveryDataBag} aware of components with already initialized common data * (e.g. on nodes prior in cluster to the one where this method is called). */ public DiscoveryDataBag bagForDataCollection() { return new DiscoveryDataBag(joiningNodeId, commonData.keySet()); } }