/*
* Copyright 2011 Edmunds.com, Inc.
*
* Licensed 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 com.edmunds.etm.management.util;
import com.edmunds.etm.management.api.HostAddress;
import com.edmunds.etm.management.api.ManagementLoadBalancerState;
import com.edmunds.etm.management.api.ManagementPoolMember;
import com.edmunds.etm.management.api.ManagementVip;
import com.edmunds.etm.management.api.ManagementVipType;
import com.edmunds.etm.management.api.ManagementVips;
import com.google.common.collect.Lists;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static com.edmunds.etm.management.api.ManagementLoadBalancerState.ACTIVE;
import static com.edmunds.etm.management.api.ManagementLoadBalancerState.CREATE_REQUEST;
import static com.edmunds.etm.management.api.ManagementLoadBalancerState.DELETE_REQUEST;
import static com.edmunds.etm.management.api.ManagementVipType.COMPLETE;
/**
* Provides the logic to delta to vips.
*/
public class VipDeltaLogic {
/**
* The logger.
*/
private static final Logger logger = Logger.getLogger(VipDeltaLogic.class);
private final ManagementVips oldVips;
private final ManagementVips newVips;
private final ManagementVipType vipsType;
private final boolean copyNew;
private static ManagementVipType calculateVipType(ManagementVips oldVips, ManagementVips newVips) {
// No old vip so new vip type wins.
if (oldVips == null) {
return newVips.getVipType();
}
final ManagementVipType oldVipsType = oldVips.getVipType();
final ManagementVipType newVipsType = newVips.getVipType();
// Use new vip type if old is complete or they are the same.
if (oldVipsType == COMPLETE || oldVipsType == newVipsType) {
return newVipsType;
}
if (newVipsType == COMPLETE) {
return oldVipsType;
}
final String msg = "Incompatible Management Vips for Delta: " + oldVips.getVipType() + " - " + newVipsType;
logger.error(msg);
throw new IllegalStateException(msg);
}
/**
* Constructs a new delta logic.
*
* @param oldVips the old tree.
* @param newVips the new tree.
* @param copyNew should identical objects be copied for the old or new trees?
*/
public VipDeltaLogic(ManagementVips oldVips, ManagementVips newVips,
boolean copyNew) {
Validate.notNull(newVips, "newVips is null.");
this.oldVips = oldVips;
this.newVips = newVips;
this.copyNew = copyNew;
this.vipsType = calculateVipType(oldVips, newVips);
}
/**
* Apply the delta operation.
*
* @return the delta result.
*/
public ManagementVips delta() {
// Result
List<ManagementVip> deltaVips = Lists.newArrayList();
if (oldVips == null) {
for (ManagementVip newVip : newVips.getVips()) {
deltaVips.add(updateState(CREATE_REQUEST, newVip));
}
} else {
// Handle when the entire vip is deleted.
for (ManagementVip oldVip : oldVips.getVips()) {
if (newVips.getVip(oldVip.getMavenModule()) == null) {
deltaVips.add(updateState(DELETE_REQUEST, oldVip));
}
}
// Handle all other cases
for (ManagementVip newVip : newVips.getVips()) {
final ManagementVip oldVip = oldVips.getVip(newVip.getMavenModule());
if (oldVip == null) {
// Doesn't exist so create it.
deltaVips.add(updateState(CREATE_REQUEST, newVip));
} else {
// Exists so check the members to see if they have changed.
deltaVips.add(deltaMembers(oldVip, newVip));
}
}
}
return new ManagementVips(vipsType, deltaVips);
}
/**
* Performs a delta of each vip's pool members.
*
* @param oldVip the old vip.
* @param newVip the new vip.
* @return a new vip with the merge of the delta
*/
private ManagementVip deltaMembers(ManagementVip oldVip, ManagementVip newVip) {
final Map<HostAddress, ManagementPoolMember> oldMembers = oldVip.getPoolMembers();
final Map<HostAddress, ManagementPoolMember> newMembers = newVip.getPoolMembers();
// Result
List<ManagementPoolMember> deltaMembers = Lists.newArrayList();
// Handle Pool Member Deletions
for (ManagementPoolMember oldMember : oldMembers.values()) {
final HostAddress hostAddress = oldMember.getHostAddress();
if (!newMembers.containsKey(hostAddress)) {
deltaMembers.add(new ManagementPoolMember(DELETE_REQUEST, hostAddress));
}
}
for (ManagementPoolMember newMember : newMembers.values()) {
final HostAddress hostAddress = newMember.getHostAddress();
if (oldMembers.containsKey(hostAddress)) {
deltaMembers.add(new ManagementPoolMember(ACTIVE, hostAddress));
} else {
deltaMembers.add(new ManagementPoolMember(CREATE_REQUEST, hostAddress));
}
}
return copyVip(ACTIVE, copyNew ? newVip : oldVip, deltaMembers);
}
private ManagementVip copyVip(
ManagementLoadBalancerState state, ManagementVip vip, Collection<ManagementPoolMember> members) {
return new ManagementVip(state, vip.getMavenModule(), vip.getHostAddress(),
members, vip.getRootContext(), vip.getRules(), vip.getHttpMonitor());
}
private ManagementVip updateState(ManagementLoadBalancerState state, ManagementVip vip) {
return copyVip(state, vip, updateState(state, vip.getPoolMembers().values()));
}
private List<ManagementPoolMember> updateState(
ManagementLoadBalancerState state, Collection<ManagementPoolMember> poolMembers) {
List<ManagementPoolMember> result = Lists.newArrayList();
for (ManagementPoolMember poolMember : poolMembers) {
result.add(new ManagementPoolMember(state, poolMember.getHostAddress()));
}
return result;
}
}