shwstppr commented on code in PR #13907:
URL: https://github.com/apache/cloudstack/pull/13907#discussion_r3901096735


##########
server/src/main/java/org/apache/cloudstack/vm/bootgroup/InstanceBootGroupApiServiceImpl.java:
##########
@@ -0,0 +1,909 @@
+// 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.cloudstack.vm.bootgroup;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import 
org.apache.cloudstack.api.command.user.bootgroup.AddMemberToInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.CreateInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.CreateInstanceBootGroupReadinessRuleCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.DeleteInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.DeleteInstanceBootGroupReadinessRuleCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupMembersCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupReadinessRulesCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.ListInstanceBootGroupsCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.RebootInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.RemoveInstanceBootGroupMemberCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.StartInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.StopInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupMemberCmd;
+import 
org.apache.cloudstack.api.command.user.bootgroup.UpdateInstanceBootGroupReadinessRuleCmd;
+import org.apache.cloudstack.api.query.dao.InstanceBootGroupJoinDao;
+import org.apache.cloudstack.api.query.vo.InstanceBootGroupJoinVO;
+import org.apache.cloudstack.api.response.InstanceBootGroupMemberChildResponse;
+import org.apache.cloudstack.api.response.InstanceBootGroupMemberResponse;
+import 
org.apache.cloudstack.api.response.InstanceBootGroupReadinessRuleResponse;
+import org.apache.cloudstack.api.response.InstanceBootGroupResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.context.CallContext;
+import 
org.apache.cloudstack.vm.bootgroup.readiness.InstanceBootGroupReadinessRule;
+import 
org.apache.cloudstack.vm.bootgroup.readiness.InstanceBootGroupReadinessRuleService;
+import org.apache.cloudstack.vm.bootgroup.readiness.ReadinessChecker;
+import org.apache.commons.lang3.EnumUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+
+import com.cloud.api.ApiResponseHelper;
+import com.cloud.event.ActionEvent;
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.projects.Project;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.component.PluggableService;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.vm.InstanceGroupVMMapVO;
+import com.cloud.vm.InstanceGroupVO;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.dao.InstanceBootGroupDao;
+import com.cloud.vm.dao.InstanceBootGroupDetailsDao;
+import com.cloud.vm.dao.InstanceBootGroupMemberDao;
+import com.cloud.vm.dao.InstanceBootGroupReadinessCheckResultDao;
+import com.cloud.vm.dao.InstanceBootGroupReadinessRuleDao;
+import com.cloud.vm.dao.InstanceBootGroupReadinessRuleDetailsDao;
+import com.cloud.vm.dao.InstanceGroupDao;
+import com.cloud.vm.dao.InstanceGroupVMMapDao;
+import com.cloud.vm.dao.UserVmDao;
+
+/**
+ * API-facing half of the Instance Boot Group feature: ACL, param validation, 
response building,
+ * command registration. Delegates orchestration/hypervisor work to {@link 
InstanceBootGroupManager}
+ * and membership eligibility checks to {@link 
InstanceBootGroupMembershipGuard}.
+ */
+@Component
+public class InstanceBootGroupApiServiceImpl implements 
InstanceBootGroupService, PluggableService {
+
+    @Inject
+    private InstanceBootGroupDao instanceBootGroupDao;
+
+    @Inject
+    private InstanceBootGroupJoinDao instanceBootGroupJoinDao;
+
+    @Inject
+    private InstanceBootGroupMemberDao instanceBootGroupMemberDao;
+
+    @Inject
+    private AccountManager accountManager;
+
+    @Inject
+    private UserVmDao userVmDao;
+
+    @Inject
+    private InstanceGroupDao instanceGroupDao;
+
+    @Inject
+    private InstanceBootGroupManager instanceBootGroupManager;
+
+    @Inject
+    private InstanceBootGroupMembershipGuard instanceBootGroupMembershipGuard;
+
+    @Inject
+    private InstanceBootGroupReadinessRuleService 
instanceBootGroupReadinessRuleService;
+
+    @Inject
+    private InstanceBootGroupReadinessRuleDao 
instanceBootGroupReadinessRuleDao;
+
+    @Inject
+    private InstanceBootGroupReadinessRuleDetailsDao 
instanceBootGroupReadinessRuleDetailsDao;
+
+    @Inject
+    private InstanceBootGroupReadinessCheckResultDao 
instanceBootGroupReadinessCheckResultDao;
+
+    @Inject
+    private InstanceBootGroupDetailsDao instanceBootGroupDetailsDao;
+
+    @Inject
+    private InstanceGroupVMMapDao instanceGroupVMMapDao;
+
+    @NotNull
+    protected InstanceBootGroupVO getGroupAndCheckAccess(long id) {
+        InstanceBootGroupVO group = instanceBootGroupDao.findById(id);
+        if (group == null) {
+            throw new InvalidParameterValueException("Unable to find instance 
boot group with ID: " + id);
+        }
+        Account caller = CallContext.current().getCallingAccount();
+        accountManager.checkAccess(caller, null, true, group);
+        return group;
+    }
+
+    protected InstanceBootGroupResponse 
createInstanceBootGroupResponse(InstanceBootGroupJoinVO bootGroup) {
+        InstanceBootGroupResponse response = new InstanceBootGroupResponse();
+        response.setId(bootGroup.getUuid());
+        response.setName(bootGroup.getName());
+        response.setDescription(bootGroup.getDescription());
+        response.setCreated(bootGroup.getCreated());
+        ApiResponseHelper.populateOwner(response, bootGroup);
+
+        String timeoutOverride = 
instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), 
InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key());
+        response.setReadinessAttemptTimeoutSeconds(timeoutOverride != null ? 
Long.parseLong(timeoutOverride) : 
InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.value());
+        String maxRetryOverride = 
instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), 
InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key());
+        response.setReadinessMaxRetryAttempts(maxRetryOverride != null ? 
Long.parseLong(maxRetryOverride) : 
InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.value());
+        String rebootOnRetryOverride = 
instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), 
InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key());
+        response.setReadinessRebootOnRetry(rebootOnRetryOverride != null ? 
Boolean.parseBoolean(rebootOnRetryOverride) : 
InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.value());
+        String initialDelayOverride = 
instanceBootGroupDetailsDao.getDetail(bootGroup.getId(), 
InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key());
+        response.setReadinessInitialDelaySeconds(initialDelayOverride != null 
? Long.parseLong(initialDelayOverride) : 
InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.value());
+
+        response.setObjectName("instancebootgroup");
+        return response;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_CREATE, 
eventDescription = "creating Instance Boot Group")
+    public InstanceBootGroup 
createInstanceBootGroup(CreateInstanceBootGroupCmd cmd) {
+        Account caller = CallContext.current().getCallingAccount();
+        Account owner = accountManager.finalizeOwner(caller, 
cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
+
+        if (instanceBootGroupDao.isNameInUse(owner.getId(), cmd.getName())) {
+            throw new InvalidParameterValueException("An instance boot group 
with name '" + cmd.getName() + "' already exists in this account");
+        }
+
+        return Transaction.execute((TransactionCallback<InstanceBootGroupVO>) 
status -> {
+            InstanceBootGroupVO group = new InstanceBootGroupVO(cmd.getName(), 
cmd.getDescription(), owner.getId(), owner.getDomainId());
+            group = instanceBootGroupDao.persist(group);
+            CallContext.current().setEventResourceId(group.getId());
+
+            if (cmd.getReadinessAttemptTimeoutSeconds() != null) {
+                setOrClearOverride(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key(), 
cmd.getReadinessAttemptTimeoutSeconds());
+            }
+            if (cmd.getReadinessMaxRetryAttempts() != null) {
+                setOrClearOverride(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key(), 
cmd.getReadinessMaxRetryAttempts());
+            }
+            if (cmd.getReadinessRebootOnRetry() != null) {
+                instanceBootGroupDetailsDao.setDetail(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key(), 
String.valueOf(cmd.getReadinessRebootOnRetry()));
+            }
+            if (cmd.getReadinessInitialDelaySeconds() != null) {
+                setOrClearOverride(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key(), 
cmd.getReadinessInitialDelaySeconds());
+            }
+
+            return group;
+        });
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_DELETE, 
eventDescription = "deleting Instance Boot Group")
+    public boolean deleteInstanceBootGroup(DeleteInstanceBootGroupCmd cmd) {
+        InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId());
+        return Transaction.execute((TransactionCallback<Boolean>) status -> {
+            instanceBootGroupMemberDao.deleteByBootGroupId(group.getId());
+            instanceBootGroupDao.remove(group.getId());
+            return true;
+        });
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_UPDATE, 
eventDescription = "updating Instance Boot Group")
+    public InstanceBootGroup 
updateInstanceBootGroup(UpdateInstanceBootGroupCmd cmd) {
+        InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId());
+
+        if (cmd.getName() != null && !Objects.equals(cmd.getName(), 
group.getName())) {
+            Account owner = accountManager.getAccount(group.getAccountId());
+            if (instanceBootGroupDao.isNameInUse(owner.getId(), 
cmd.getName())) {
+                throw new InvalidParameterValueException("An instance boot 
group with name '" + cmd.getName() + "' already exists in this account");
+            }
+            group.setName(cmd.getName());
+        }
+        if (cmd.getDescription() != null) {
+            group.setDescription(cmd.getDescription());
+        }
+
+        return Transaction.execute((TransactionCallback<InstanceBootGroupVO>) 
status -> {
+            if (cmd.getReadinessAttemptTimeoutSeconds() != null) {
+                setOrClearOverride(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessAttemptTimeoutSeconds.key(), 
cmd.getReadinessAttemptTimeoutSeconds());
+            }
+            if (cmd.getReadinessMaxRetryAttempts() != null) {
+                setOrClearOverride(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessMaxRetryAttempts.key(), 
cmd.getReadinessMaxRetryAttempts());
+            }
+            if (cmd.getReadinessRebootOnRetry() != null) {
+                instanceBootGroupDetailsDao.setDetail(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessRebootOnRetry.key(), 
String.valueOf(cmd.getReadinessRebootOnRetry()));
+            }
+            if (cmd.getReadinessInitialDelaySeconds() != null) {
+                setOrClearOverride(group.getId(), 
InstanceBootGroupManagerImpl.ReadinessInitialDelaySeconds.key(), 
cmd.getReadinessInitialDelaySeconds());
+            }
+
+            instanceBootGroupDao.update(group.getId(), group);
+            return instanceBootGroupDao.findById(group.getId());
+        });
+    }
+
+    private void setOrClearOverride(long bootGroupId, String key, long value) {
+        if (value < 0) {
+            instanceBootGroupDetailsDao.setDetail(bootGroupId, key, null);
+        } else {
+            instanceBootGroupDetailsDao.setDetail(bootGroupId, key, 
String.valueOf(value));
+        }
+    }
+
+    @Override
+    public ListResponse<InstanceBootGroupResponse> 
listInstanceBootGroups(ListInstanceBootGroupsCmd cmd) {
+        final CallContext ctx = CallContext.current();
+        final Account caller = ctx.getCallingAccount();
+        final Long id = cmd.getId();
+        final String keyword = cmd.getKeyword();
+
+        List<InstanceBootGroupResponse> responsesList = new ArrayList<>();
+        List<Long> permittedAccounts = new ArrayList<>();
+        Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> 
domainIdRecursiveListProject =
+                new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null);
+        accountManager.buildACLSearchParameters(caller, id, 
cmd.getAccountName(), cmd.getProjectId(),
+                permittedAccounts, domainIdRecursiveListProject, 
cmd.listAll(), false);
+        Long domainId = domainIdRecursiveListProject.first();
+        Boolean isRecursive = domainIdRecursiveListProject.second();
+        Project.ListProjectResourcesCriteria listProjectResourcesCriteria = 
domainIdRecursiveListProject.third();
+
+        Filter searchFilter = new Filter(InstanceBootGroupJoinVO.class, "id", 
true, cmd.getStartIndex(),
+                cmd.getPageSizeVal());
+        SearchBuilder<InstanceBootGroupJoinVO> sb = 
instanceBootGroupJoinDao.createSearchBuilder();
+        accountManager.buildACLSearchBuilder(sb, domainId, isRecursive, 
permittedAccounts,
+                listProjectResourcesCriteria);
+        sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
+        sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
+        sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE);
+        SearchCriteria<InstanceBootGroupJoinVO> sc = sb.create();
+        accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, 
permittedAccounts,
+                listProjectResourcesCriteria);
+        if (keyword != null) {
+            sc.setParameters("keyword", "%" + keyword + "%");
+        }
+        if (id != null) {
+            sc.setParameters("id", id);
+        }
+        Pair<List<InstanceBootGroupJoinVO>, Integer> bootGroupsAndCount = 
instanceBootGroupJoinDao.searchAndCount(sc, searchFilter);
+        for (InstanceBootGroupJoinVO bootGroup : bootGroupsAndCount.first()) {
+            InstanceBootGroupResponse response = 
createInstanceBootGroupResponse(bootGroup);
+            responsesList.add(response);
+        }
+        ListResponse<InstanceBootGroupResponse> response = new 
ListResponse<>();
+        response.setResponses(responsesList, bootGroupsAndCount.second());
+        return response;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_INSTANCE_BOOT_GROUP_MEMBER_ADD, 
eventDescription = "adding Instance Boot Group member")
+    public InstanceBootGroupMember 
addMemberToInstanceBootGroup(AddMemberToInstanceBootGroupCmd cmd) {
+        InstanceBootGroupVO group = getGroupAndCheckAccess(cmd.getId());
+
+        if (cmd.getOrder() < 0) {
+            throw new InvalidParameterValueException("Order value must be 0 or 
greater");
+        }
+
+        InstanceBootGroupMember.MemberType memberType;
+        long memberId;
+
+        
validateEitherVirtualMachineIdOrInstanceGroupIdParam(cmd.getVirtualMachineId(), 
cmd.getInstanceGroupId());
+
+        if (cmd.getVirtualMachineId() != null) {
+            UserVm vm = getValidatedVmForAddMember(group, 
cmd.getVirtualMachineId());
+            memberType = InstanceBootGroupMember.MemberType.VirtualMachine;
+            memberId = vm.getId();
+        } else {
+            InstanceGroupVO instanceGroup = 
getValidatedInstanceGroupAddMember(group, cmd.getInstanceGroupId());
+            memberType = InstanceBootGroupMember.MemberType.InstanceGroup;
+            memberId = instanceGroup.getId();
+        }
+
+        if (instanceBootGroupMemberDao.findByMember(memberType, memberId) != 
null) {
+            throw new InvalidParameterValueException(String.format("This %s 
already belongs to an instance boot group", memberType.name()));
+        }
+
+        List<InstanceBootGroupMemberVO> siblings = 
instanceBootGroupMemberDao.listByBootGroupId(group.getId());
+        long maxMembers = 
InstanceBootGroupManagerImpl.MaxMembersPerBootGroup.valueIn(group.getDomainId());
+        if (siblings.size() >= maxMembers) {
+            throw new InvalidParameterValueException(String.format(
+                    "Instance boot group %s already has the maximum of %d 
member(s) allowed", group, maxMembers));
+        }
+
+        shiftSiblingOrdersForInsert(siblings, cmd.getOrder());
+        InstanceBootGroupMemberVO member = new 
InstanceBootGroupMemberVO(group.getId(), memberType, memberId, cmd.getOrder());
+        return instanceBootGroupMemberDao.persist(member);
+    }
+
+    /**
+     * Makes room for a new member at {@code order} by bumping every existing 
member already at or
+     * past it up by one slot, rather than letting the new member silently 
share that order.
+     */
+    private void shiftSiblingOrdersForInsert(List<InstanceBootGroupMemberVO> 
siblings, int order) {
+        for (InstanceBootGroupMemberVO sibling : siblings) {
+            if (sibling.getOrder() >= order) {
+                sibling.setOrder(sibling.getOrder() + 1);
+                instanceBootGroupMemberDao.update(sibling.getId(), sibling);
+            }
+        }
+    }
+
+    @NotNull
+    private UserVm getValidatedVmForAddMember(InstanceBootGroupVO group, long 
virtualMachineId) {
+        UserVm vm = userVmDao.findById(virtualMachineId);
+        if (vm == null) {
+            throw new InvalidParameterValueException("Unable to find virtual 
machine with ID: " + virtualMachineId);
+        }
+        validateMemberAccount(vm.getAccountId(), group.getAccountId());
+        
instanceBootGroupMembershipGuard.validateVmEligibleForGroupMembership(vm.getId());
+        return vm;
+    }
+
+    @NotNull
+    private InstanceGroupVO 
getValidatedInstanceGroupAddMember(InstanceBootGroupVO group, long 
instanceGroupId) {
+        InstanceGroupVO instanceGroup = 
instanceGroupDao.findById(instanceGroupId);
+        if (instanceGroup == null || instanceGroup.getRemoved() != null) {
+            throw new InvalidParameterValueException("Unable to find instance 
group with ID: " + instanceGroupId);
+        }
+        validateMemberAccount(instanceGroup.getAccountId(), 
group.getAccountId());
+        
instanceBootGroupMembershipGuard.validateInstanceGroupEligibleForBootGroupMembership(instanceGroup.getId());
+        return instanceGroup;
+    }
+
+    protected static void 
validateEitherVirtualMachineIdOrInstanceGroupIdParam(Long virtualMachineId, 
Long instanceGroupId) {
+        if (virtualMachineId != null && instanceGroupId != null) {
+            throw new InvalidParameterValueException("Only one of 
virtualmachineid or instancegroupid may be specified");
+        }
+        if (virtualMachineId == null && instanceGroupId == null) {
+            throw new InvalidParameterValueException("Either virtualmachineid 
or instancegroupid must be specified");
+        }
+    }
+
+    @Override
+    @ActionEvent(eventType = 
EventTypes.EVENT_INSTANCE_BOOT_GROUP_MEMBER_REMOVE, eventDescription = 
"removing Instance Boot Group member")
+    public boolean 
removeInstanceBootGroupMember(RemoveInstanceBootGroupMemberCmd cmd) {
+        InstanceBootGroupMemberVO member = 
instanceBootGroupMemberDao.findById(cmd.getId());
+        if (member == null) {
+            throw new InvalidParameterValueException("Unable to find boot 
group member with ID: " + cmd.getId());
+        }
+        getGroupAndCheckAccess(member.getBootGroupId());
+        instanceBootGroupMemberDao.expunge(member.getId());
+        return true;
+    }
+
+    @Override
+    @ActionEvent(eventType = 
EventTypes.EVENT_INSTANCE_BOOT_GROUP_MEMBER_REORDER, eventDescription = 
"reordering Instance Boot Group member")
+    public InstanceBootGroupMember 
updateInstanceBootGroupMember(UpdateInstanceBootGroupMemberCmd cmd) {
+        InstanceBootGroupMemberVO member = 
instanceBootGroupMemberDao.findById(cmd.getId());
+        if (member == null) {
+            throw new InvalidParameterValueException("Unable to find boot 
group member with ID: " + cmd.getId());
+        }
+        int newOrder = cmd.getOrder();
+        if (newOrder < 0) {
+            throw new InvalidParameterValueException("Order value must be 0 or 
greater");
+        }
+        getGroupAndCheckAccess(member.getBootGroupId());
+
+        int oldOrder = member.getOrder();
+        if (newOrder != oldOrder) {
+            shiftSiblingOrders(member, oldOrder, newOrder);
+            member.setOrder(newOrder);
+            instanceBootGroupMemberDao.update(member.getId(), member);
+        }
+        return instanceBootGroupMemberDao.findById(member.getId());
+    }
+
+    /**
+     * Shifts every other member between the old and new position by one slot 
— list-reorder
+     * semantics, not just moving the single member whose order was explicitly 
given.
+     */
+    private void shiftSiblingOrders(InstanceBootGroupMemberVO member, int 
oldOrder, int newOrder) {
+        List<InstanceBootGroupMemberVO> siblings = 
instanceBootGroupMemberDao.listByBootGroupId(member.getBootGroupId());

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to