/* * Copyright 2014 University of Southern California * * 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 edu.usc.pgroup.floe.resourcemanager; import edu.usc.pgroup.floe.thriftgen.TEdge; import edu.usc.pgroup.floe.thriftgen.TFloeApp; import edu.usc.pgroup.floe.thriftgen.TPellet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author kumbhare */ public class ResourceMappingDelta implements Serializable { /** * Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(ResourceMappingDelta.class); /** * The floe application. */ private final TFloeApp floeApp; /** * Redundant list of all modified containers. (since the last reset). */ private final List<String> modifiedContainers; /** * Map from container id to flakes for which the predecessors flakes have * been added or removed. */ private final Map<String, Map<String, FlakeInstanceDelta>> addedFlakes; /** * A redundant map to store a mapping from pid to a list of all flakes * added across containers. DO THE SAME FOR REMOVED FLAKES TOO. */ private final Map<String, List<FlakeInstanceDelta>> pidToNewFlakeMap; /** * Map from container id to updated flakes * (added or removed pellet instance). */ private final Map<String, Map<String, FlakeInstanceDelta>> updatedFlakes; /** * Map from container id to removed flakes * (and all pellets within that flake). */ private final Map<String, Map<String, FlakeInstanceDelta>> removedFlakes; /** * constructor. * @param app Application to which this Resource mapping belongs. */ public ResourceMappingDelta(final TFloeApp app) { this.addedFlakes = new HashMap<>(); this.updatedFlakes = new HashMap<>(); this.removedFlakes = new HashMap<>(); this.pidToNewFlakeMap = new HashMap<>(); this.modifiedContainers = new ArrayList<>(); this.floeApp = app; } /** * resets the delta back to empty so that next iteration of changes may * be applied. */ public final void reset() { this.addedFlakes.clear(); this.updatedFlakes.clear(); this.removedFlakes.clear(); this.pidToNewFlakeMap.clear(); this.modifiedContainers.clear(); } /** * called whenever a new flake is added to the resource mapping. * @param fl the flake instance that was added */ public final void flakeAdded(final ResourceMapping.FlakeInstance fl) { Map<String, FlakeInstanceDelta> flMap = null; String containerId = fl.getContainerId(); if (!modifiedContainers.contains(containerId)) { modifiedContainers.add(containerId); } if (addedFlakes.containsKey(containerId)) { flMap = addedFlakes.get(containerId); } else { flMap = new HashMap<String, FlakeInstanceDelta>(); addedFlakes.put(containerId, flMap); } if (!flMap.containsKey(fl.getCorrespondingPelletId())) { FlakeInstanceDelta fldelta = new FlakeInstanceDelta(fl); flMap.put(fl.getCorrespondingPelletId(), fldelta); List<FlakeInstanceDelta> flList = null; if (pidToNewFlakeMap.containsKey(fl.getCorrespondingPelletId())) { flList = pidToNewFlakeMap.get( fl.getCorrespondingPelletId() ); } else { flList = new ArrayList<>(); pidToNewFlakeMap.put( fl.getCorrespondingPelletId(), flList ); } flList.add(fldelta); } } /** * called when a flake is updated by the resource manager. * @param fl the flake instance that was updated. * @param type the update type (i.e. instance added or removed). */ public final void flakeUpdated(final ResourceMapping.FlakeInstance fl, final UpdateType type) { Map<String, FlakeInstanceDelta> flMap = null; String containerId = fl.getContainerId(); if (!modifiedContainers.contains(containerId)) { modifiedContainers.add(containerId); } //check if the flake was newly added/or already removed or udpdated? if (addedFlakes.containsKey(containerId)) { flMap = addedFlakes.get(containerId); } else if (updatedFlakes.containsKey(containerId)) { flMap = updatedFlakes.get(containerId); } else if (removedFlakes.containsKey(containerId)) { //No need for // the removed flakes check. doesnt make sense. flMap = removedFlakes.get(containerId); } if (flMap == null) { flMap = new HashMap<String, FlakeInstanceDelta>(); updatedFlakes.put(containerId, flMap); } if (!flMap.containsKey(fl.getCorrespondingPelletId())) { flMap.put(fl.getCorrespondingPelletId(), new FlakeInstanceDelta(fl)); } FlakeInstanceDelta flDelta = flMap.get(fl.getCorrespondingPelletId()); if (type == UpdateType.InstanceAdded) { flDelta.instanceAdded(); } else if (type == UpdateType.InstanceRemoved) { LOGGER.info("Removing pellet instance"); flDelta.instanceRemoved(); LOGGER.info("Removed pellet instance: {}", flDelta.getNumInstancesRemoved()); } else if (type == UpdateType.ActiveAlternateChanged) { flDelta.activeAlternateChanged(); } } /** * Flake removed from resource mapping. * @param fl the flake instance that was removed. */ public final void flakeRemoved(final ResourceMapping.FlakeInstance fl) { Map<String, FlakeInstanceDelta> flMap = null; String containerId = fl.getContainerId(); if (!modifiedContainers.contains(containerId)) { modifiedContainers.add(containerId); } if (removedFlakes.containsKey(containerId)) { flMap = removedFlakes.get(containerId); } else { flMap = new HashMap<String, FlakeInstanceDelta>(); removedFlakes.put(containerId, flMap); } if (!flMap.containsKey(fl.getCorrespondingPelletId())) { flMap.put(fl.getCorrespondingPelletId(), new FlakeInstanceDelta(fl)); } } /** * @return string representation of the resource mapping delta. */ @Override public final String toString() { StringBuilder builder = new StringBuilder(); builder.append("\nNewly added Flakes: {"); for (Map.Entry<String, Map<String, FlakeInstanceDelta>> added : addedFlakes.entrySet()) { String containerId = added.getKey(); builder.append(containerId + "=>["); for (Map.Entry<String, FlakeInstanceDelta> fld : added.getValue().entrySet()) { builder.append(fld.getKey() + ":" + fld.getValue() .getNumInstancesAdded() + ", " + fld.getValue().getNumInstancesRemoved() + "; "); } builder.append("]"); } builder.append("}\nUpdated Flakes: {"); for (Map.Entry<String, Map<String, FlakeInstanceDelta>> added : updatedFlakes.entrySet()) { String containerId = added.getKey(); builder.append(containerId + "=>["); for (Map.Entry<String, FlakeInstanceDelta> fld : added.getValue().entrySet()) { builder.append(fld.getKey() + ":" + fld.getValue() .getNumInstancesAdded() + ", " + fld.getValue().getNumInstancesRemoved() + "; "); } builder.append("]"); } builder.append("}\nRemoved Flakes: {"); for (Map.Entry<String, Map<String, FlakeInstanceDelta>> added : removedFlakes.entrySet()) { String containerId = added.getKey(); builder.append(containerId + "=>["); for (Map.Entry<String, FlakeInstanceDelta> fld : added.getValue().entrySet()) { builder.append(fld.getKey() + ":" + fld.getValue() .getNumInstancesAdded() + ", " + fld.getValue().getNumInstancesRemoved() + "; "); } builder.append("]"); } builder.append("}"); return builder.toString(); } /** * //Changing to private, since use of this function is discouraged. * @param containerId container id. * @return true if the container has been updated. */ private boolean isContainerUpdated(final String containerId) { if (addedFlakes.containsKey(containerId)) { return true; } else if (updatedFlakes.containsKey(containerId)) { return true; } else if (removedFlakes.containsKey(containerId)) { return true; } return false; } /** * @param containerId container id. * @return only the newly added flakes (and any pellet instances add to * that flake). */ public final Map<String, FlakeInstanceDelta> getNewlyAddedFlakes( final String containerId) { if (addedFlakes.containsKey(containerId)) { return addedFlakes.get(containerId); } return null; } /** * @param containerId container id. * @return the flakes that have been updated (and any pellet instances * add/removed to that flake). */ public final Map<String, FlakeInstanceDelta> getUpdatedFlakes( final String containerId) { if (updatedFlakes.containsKey(containerId)) { return updatedFlakes.get(containerId); } return null; } /** * @param containerId container id. * @return the flakes that have been removed */ public final Map<String, FlakeInstanceDelta> getRemovedFlakes( final String containerId) { if (removedFlakes.containsKey(containerId)) { return removedFlakes.get(containerId); } return null; } /** * Gets the list of ONLY NEWLY ADDED preceding flakes to which the given * flake should connect to. * @param pid the pellet id. * @return list of preceding flakes to which the given flake should connect. */ public final List<FlakeInstanceDelta> getNewlyAddedPrecedingFlakes( final String pid) { List<FlakeInstanceDelta> precedingFlakes = new ArrayList<>(); TPellet tPellet = floeApp.get_pellets().get(pid); for (TEdge edge: tPellet.get_incomingEdges()) { String srcPid = edge.get_srcPelletId(); if (pidToNewFlakeMap != null && pidToNewFlakeMap.containsKey(srcPid)) { precedingFlakes.addAll(pidToNewFlakeMap.get(srcPid)); } } return precedingFlakes; } /** * again changing to private because its use is discouraged. * @return the number of containers that have been updated. */ private int getContainersToUpdate() { return modifiedContainers.size(); } /** * class to represent the flake instance delta since the last reset. */ public class FlakeInstanceDelta implements Serializable { /** * number of instances added. */ private int numInstancesAdded; /** * number of instances removed. */ private int numInstancesRemoved; /** * number of instances removed. */ private boolean activeAlternateChanged; /** * flake instance. */ private final ResourceMapping.FlakeInstance flakeInstance; /** * Constructor. * @param instance Flake instance. */ public FlakeInstanceDelta( final ResourceMapping.FlakeInstance instance) { this.flakeInstance = instance; this.numInstancesAdded = 0; this.numInstancesRemoved = 0; this.activeAlternateChanged = false; } /** * called when an instance is added. */ public final void instanceAdded() { this.numInstancesAdded++; } /** * called when an instance is removed. */ public final void instanceRemoved() { this.numInstancesRemoved++; } /** * flag this flake as the active alternate has changed. */ public final void activeAlternateChanged() { this.activeAlternateChanged = true; } /** * @return number of instances added to this flake. */ public final int getNumInstancesAdded() { return this.numInstancesAdded; } /** * @return number of instances removed to this flake. */ public final int getNumInstancesRemoved() { return this.numInstancesRemoved; } /** * @return true if the alternate for this flake has changed. Use the * TFloeApp to get the actual active alternate. */ public final boolean isAlternateChanged() { return activeAlternateChanged; } /** * @return the corresponding flake instance. */ public final ResourceMapping.FlakeInstance getFlakeInstance() { return flakeInstance; } } /** * Enum to describe the type of flake update. */ public enum UpdateType { /** * Instance added update type. */ InstanceAdded, /** * Instance removed update type. */ InstanceRemoved, /** * Active Alternate changed update type. */ ActiveAlternateChanged } }