package org.jgroups; import org.jgroups.annotations.Immutable; import org.jgroups.util.Util; import java.io.DataInput; import java.io.DataOutput; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Supplier; /** * A view that is sent as a result of a cluster merge. Whenever a group splits into subgroups, e.g., due to * a network partition, and later the subgroups merge back together, a MergeView instead of a View * will be received by the application. The MergeView class is a subclass of View and contains as * additional instance variable: the list of views that were merged. For example, if the group * denoted by view V1:(p,q,r,s,t) splits into subgroups V2:(p,q,r) and V2:(s,t), the merged view * might be V3:(p,q,r,s,t). In this case the MergeView would contain a list of 2 views: V2:(p,q,r) * and V2:(s,t). * * @since 2.0 * @author Bela Ban */ @Immutable public class MergeView extends View { protected View[] subgroups; // subgroups that merged into this single view (a list of Views) public MergeView() { // Used by externalization } /** * Creates a new merge view * * @param view_id The view id of this view (can not be null) * @param members Contains a list of all the members in the view, can be empty but not null. * @param subgroups A list of Views representing the former subgroups */ public MergeView(ViewId view_id, List<Address> members, List<View> subgroups) { super(view_id, members); this.subgroups=listToArray(subgroups); } public MergeView(ViewId view_id, Address[] members, List<View> subgroups) { super(view_id, members); this.subgroups=listToArray(subgroups); } /** * Creates a new view * * @param creator The creator of this view (can not be null) * @param id The lamport timestamp of this view * @param members Contains a list of all the members in the view, can be empty but not null. * @param subgroups A list of Views representing the former subgroups */ public MergeView(Address creator, long id, List<Address> members, List<View> subgroups) { super(creator, id, members); this.subgroups=listToArray(subgroups); } public Supplier<? extends MergeView> create() { return MergeView::new; } public List<View> getSubgroups() { return Collections.unmodifiableList(Arrays.asList(subgroups)); } public MergeView copy() { return this; } public boolean deepEquals(View other) { if(!super.deepEquals(other)) return false; if(!(other instanceof MergeView)) return true; MergeView other_view=(MergeView)other; if(subgroups == null && other_view.subgroups == null) return true; if(subgroups.length != other_view.subgroups.length) return false; for(int i=0; i < subgroups.length; i++) { View my_view=subgroups[i], oth_view=other_view.subgroups[i]; if(!my_view.deepEquals(oth_view)) return false; } return true; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("MergeView::").append(super.toString()); if(subgroups != null && subgroups.length > 0) { sb.append(", ").append(subgroups.length).append(" subgroups: "); sb.append(Util.printListWithDelimiter(subgroups, ", ", Util.MAX_LIST_PRINT_SIZE)); } return sb.toString(); } public void writeTo(DataOutput out) throws Exception { super.writeTo(out); // write subgroups int len=subgroups != null? subgroups.length : 0; out.writeShort(len); if(len == 0) return; for(View v: subgroups) { int index=get(v.getCreator()); out.writeShort(index); // if we don't find the member, write the addres (https://issues.jboss.org/browse/JGRP-1707) if(index < 0) Util.writeAddress(v.getCreator(), out); out.writeLong(v.getViewId().getId()); int num_mbrs=v.size(); out.writeShort(num_mbrs); for(Address mbr: v) { index=get(mbr); out.writeShort(index); if(index < 0) Util.writeAddress(mbr, out); } } } public void readFrom(DataInput in) throws Exception { super.readFrom(in); short len=in.readShort(); if(len > 0) { subgroups=new View[len]; for(int i=0; i < len; i++) { int index=in.readShort(); Address creator=index >= 0 ? get(index) : Util.readAddress(in); long id=in.readLong(); Address[] mbrs=new Address[in.readShort()]; for(int j=0; j < mbrs.length; j++) { index=in.readShort(); mbrs[j]=index >= 0? get(index) : Util.readAddress(in); } try { View view=View.create(creator, id, mbrs); subgroups[i]=view; } catch(Throwable t) { } } } } public int serializedSize() { int retval=super.serializedSize(); retval+=Global.SHORT_SIZE; // for size of subgroups vector if(subgroups == null) return retval; for(View v: subgroups) { retval+=Global.SHORT_SIZE + Global.LONG_SIZE // creator and ID (ViewId) + Global.SHORT_SIZE; // number of members in the subview Address creator=v.getCreator(); if(get(creator) < 0) retval+=Util.size(creator); retval+=v.size() * Global.SHORT_SIZE; // a short (index) for each member for(Address sub_mbr: v) { int index=get(sub_mbr); if(index < 0) retval+=Util.size(sub_mbr); } } return retval; } protected static View[] listToArray(List<View> list) { if(list == null) return null; View[] retval=new View[list.size()]; int index=0; for(View view: list) retval[index++]=view; return retval; } protected int get(Address member) { if(member == null) return -1; for(int i=0; i < members.length; i++) if(member.equals(members[i])) return i; return -1; } protected Address get(int index) { return members != null && index >= 0 && index < members.length? members[index] : null; } }