| package org.apache.helix.model.builder; |
| |
| /* |
| * 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. |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.helix.model.Transition; |
| |
| public class StateTransitionTableBuilder { |
| // for convenient get path value, in which non-exist means MAX |
| static int getPathVal(Map<String, Map<String, Integer>> path, String fromState, String toState) { |
| if (!path.containsKey(fromState)) { |
| return Integer.MAX_VALUE; |
| } |
| |
| if (!(path.get(fromState).containsKey(toState))) { |
| return Integer.MAX_VALUE; |
| } |
| |
| return path.get(fromState).get(toState); |
| } |
| |
| static void setPathVal(Map<String, Map<String, Integer>> path, String fromState, String toState, |
| int val) { |
| if (!path.containsKey(fromState)) { |
| path.put(fromState, new HashMap<String, Integer>()); |
| } |
| |
| path.get(fromState).put(toState, val); |
| } |
| |
| static void setNext(Map<String, Map<String, String>> next, String fromState, String toState, |
| String nextState) { |
| if (!next.containsKey(fromState)) { |
| next.put(fromState, new HashMap<String, String>()); |
| } |
| |
| next.get(fromState).put(toState, nextState); |
| |
| } |
| |
| /** |
| * auxiliary method to get next state based on next map |
| * @param next |
| * @param fromState |
| * @param toState |
| * @return nextState or null if doesn't exist a path |
| */ |
| public static String getNext(Map<String, Map<String, String>> next, String fromState, |
| String toState) { |
| if (!next.containsKey(fromState)) { |
| // no path |
| return null; |
| } |
| |
| return next.get(fromState).get(toState); |
| } |
| |
| // debug |
| static void printPath(List<String> states, Map<String, Map<String, String>> next) { |
| for (String fromState : states) { |
| for (String toState : states) { |
| if (toState.equals(fromState)) { |
| // not print self-loop |
| continue; |
| } |
| |
| System.out.print(fromState); |
| String nextState = getNext(next, fromState, toState); |
| while (nextState != null && !nextState.equals(toState)) { |
| System.out.print("->" + nextState); |
| nextState = getNext(next, nextState, toState); |
| } |
| |
| if (nextState == null) { |
| // no path between fromState -> toState |
| System.out.println("->null" + toState + " (no path avaliable)"); |
| } else { |
| System.out.println("->" + toState); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Uses floyd-warshall algorithm, shortest distance for all pair of nodes |
| * Allows one to lookup nextState given fromState,toState <br/> |
| * map.get(fromState).get(toState) --> nextState |
| * @param states |
| * @param transitions |
| * @return next map |
| */ |
| public Map<String, Map<String, String>> buildTransitionTable(List<String> states, |
| List<Transition> transitions) { |
| // path distance value |
| Map<String, Map<String, Integer>> path = new HashMap<String, Map<String, Integer>>(); |
| |
| // next state |
| Map<String, Map<String, String>> next = new HashMap<String, Map<String, String>>(); |
| |
| // init path and next |
| for (String state : states) { |
| setPathVal(path, state, state, 0); |
| setNext(next, state, state, state); |
| } |
| |
| for (Transition transition : transitions) { |
| String fromState = transition.getFromState(); |
| String toState = transition.getToState(); |
| setPathVal(path, fromState, toState, 1); |
| setNext(next, fromState, toState, toState); |
| } |
| |
| // iterate |
| for (String intermediateState : states) { |
| for (String fromState : states) { |
| for (String toState : states) { |
| int pathVal1 = getPathVal(path, fromState, intermediateState); |
| int pathVal2 = getPathVal(path, intermediateState, toState); |
| int pathValCur = getPathVal(path, fromState, toState); |
| |
| // should not overflow |
| if (pathVal1 < Integer.MAX_VALUE && pathVal2 < Integer.MAX_VALUE |
| && (pathVal1 + pathVal2) < pathValCur) { |
| setPathVal(path, fromState, toState, pathVal1 + pathVal2); |
| setNext(next, fromState, toState, getNext(next, fromState, intermediateState)); |
| } |
| } |
| } |
| } |
| return next; |
| } |
| |
| // TODO move this to test |
| public static void main(String[] args) { |
| List<String> states = new ArrayList<String>(); |
| // [MASTER, SLAVE, DROPPED, OFFLINE] |
| // [SLAVE-OFFLINE, OFFLINE-SLAVE, SLAVE-MASTER, OFFLINE-DROPPED, MASTER-SLAVE] |
| states.add("MASTER"); |
| states.add("SLAVE"); |
| states.add("DROPPED"); |
| states.add("OFFLINE"); |
| |
| List<Transition> transitions = new ArrayList<Transition>(); |
| transitions.add(new Transition("SLAVE", "OFFLINE")); |
| transitions.add(new Transition("OFFLINE", "SLAVE")); |
| transitions.add(new Transition("SLAVE", "MASTER")); |
| transitions.add(new Transition("OFFLINE", "DROPPED")); |
| transitions.add(new Transition("MASTER", "SLAVE")); |
| |
| StateTransitionTableBuilder builder = new StateTransitionTableBuilder(); |
| Map<String, Map<String, String>> next = builder.buildTransitionTable(states, transitions); |
| System.out.println(next); |
| printPath(states, next); |
| } |
| } |