/* * Copyright 2015 LinkedIn Corp. * * 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 azkaban.executor; import azkaban.executor.selector.CandidateComparator; import azkaban.executor.selector.CandidateFilter; import azkaban.executor.selector.CandidateSelector; import azkaban.executor.selector.ExecutorComparator; import azkaban.executor.selector.ExecutorFilter; import azkaban.executor.selector.ExecutorSelector; import azkaban.executor.selector.FactorComparator; import azkaban.executor.selector.FactorFilter; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.BasicConfigurator; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class SelectorTest { // test samples. protected ArrayList<MockExecutorObject> executorList = new ArrayList<>(); @BeforeClass public static void onlyOnce() { BasicConfigurator.configure(); } @Before public void setUp() throws Exception { this.executorList.clear(); this.executorList .add(new MockExecutorObject("Executor1", 8080, 50.0, 2048, 5, new Date(), 20, 6400)); this.executorList .add(new MockExecutorObject("Executor2", 8080, 50.0, 2048, 4, new Date(), 20, 6400)); this.executorList .add(new MockExecutorObject("Executor3", 8080, 40.0, 2048, 1, new Date(), 20, 6400)); this.executorList .add(new MockExecutorObject("Executor4", 8080, 50.0, 2048, 4, new Date(), 20, 6400)); this.executorList .add(new MockExecutorObject("Executor5", 8080, 50.0, 1024, 5, new Date(), 90, 6400)); this.executorList .add(new MockExecutorObject("Executor6", 8080, 50.0, 1024, 5, new Date(), 90, 3200)); this.executorList .add(new MockExecutorObject("Executor7", 8080, 50.0, 1024, 5, new Date(), 90, 3200)); this.executorList .add(new MockExecutorObject("Executor8", 8080, 50.0, 2048, 1, new Date(), 90, 3200)); this.executorList .add(new MockExecutorObject("Executor9", 8080, 50.0, 2050, 5, new Date(), 90, 4200)); this.executorList .add(new MockExecutorObject("Executor10", 8080, 00.0, 1024, 1, new Date(), 90, 3200)); this.executorList .add(new MockExecutorObject("Executor11", 8080, 20.0, 2096, 3, new Date(), 90, 2400)); this.executorList .add(new MockExecutorObject("Executor12", 8080, 90.0, 2050, 5, new Date(), 60, 2500)); // make sure each time the order is different. Collections.shuffle(this.executorList); } private MockExecutorObject getExecutorByName(final String name) { MockExecutorObject returnVal = null; for (final MockExecutorObject item : this.executorList) { if (item.name.equals(name)) { returnVal = item; break; } } return returnVal; } @After public void tearDown() throws Exception { } @Test public void testExecutorFilter() throws Exception { // mock object, remaining memory 11500, total memory 3095, remainingTmpSpace 4200, priority 2. final MockFlowObject dispatchingObj = new MockFlowObject("flow1", 3096, 1500, 4200, 2); final MockFilter mFilter = new MockFilter(); mFilter.registerFilterforRemainingMemory(); // expect true. boolean result = mFilter.filterTarget(this.getExecutorByName("Executor1"), dispatchingObj); Assert.assertTrue(result); //expect true. result = mFilter.filterTarget(this.getExecutorByName("Executor3"), dispatchingObj); /* 1 [main] INFO azkaban.executor.Selector.CandidateFilter - start checking 'Executor3' with factor filter for 'Mockfilter' 2 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredRemainingMemory] filter result : true 2 [main] INFO azkaban.executor.Selector.CandidateFilter - Final checking result : true */ Assert.assertTrue(result); // add the priority filter. mFilter.registerFilterforPriority(); result = mFilter.filterTarget(this.getExecutorByName("Executor3"), dispatchingObj); // expect false, for priority. /* 2 [main] INFO azkaban.executor.Selector.CandidateFilter - start checking 'Executor3' with factor filter for 'Mockfilter' 2 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredRemainingMemory] filter result : true 2 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredProprity] filter result : false 2 [main] INFO azkaban.executor.Selector.CandidateFilter - Final checking result : false */ Assert.assertFalse(result); // add the remaining space filter. mFilter.registerFilterforRemainingTmpSpace(); // expect pass. result = mFilter.filterTarget(this.getExecutorByName("Executor2"), dispatchingObj); /* 3 [main] INFO azkaban.executor.Selector.CandidateFilter - start checking 'Executor2' with factor filter for 'Mockfilter' 3 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredRemainingMemory] filter result : true 3 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredRemainingTmpSpace] filter result : true 3 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredProprity] filter result : true 3 [main] INFO azkaban.executor.Selector.CandidateFilter - Final checking result : true */ Assert.assertTrue(result); // expect false, remaining tmp, priority will also fail but the logic shortcuts when the Tmp size check Fails. result = mFilter.filterTarget(this.getExecutorByName("Executor8"), dispatchingObj); /* 4 [main] INFO azkaban.executor.Selector.CandidateFilter - start checking 'Executor8' with factor filter for 'Mockfilter' 4 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredRemainingMemory] filter result : true 4 [main] INFO azkaban.executor.Selector.CandidateFilter - [Factor: requiredRemainingTmpSpace] filter result : false 4 [main] INFO azkaban.executor.Selector.CandidateFilter - Final checking result : false */ Assert.assertFalse(result); } @Test public void testExecutorFilterWithNullInputs() throws Exception { final MockFilter filter = new MockFilter(); filter.registerFilterforPriority(); filter.registerFilterforRemainingMemory(); filter.registerFilterforRemainingTmpSpace(); filter.registerFilterforTotalMemory(); boolean result = false; try { result = filter.filterTarget(this.getExecutorByName("Executor1"), null); } catch (final Exception ex) { Assert.fail("no exception should be thrown when null value is passed to the filter."); } // note : the FactorFilter logic will decide whether true or false should be returned when null value // is passed, for the Mock class it returns false. Assert.assertFalse(result); try { result = filter.filterTarget(null, null); } catch (final Exception ex) { Assert.fail("no exception should be thrown when null value is passed to the filter."); } // note : the FactorFilter logic will decide whether true or false should be returned when null value // is passed, for the Mock class it returns false. Assert.assertFalse(result); } @Test public void testExecutorComparer() throws Exception { final MockComparator comparator = new MockComparator(); comparator.registerComparerForMemory(5); MockExecutorObject nextExecutor = Collections.max(this.executorList, comparator); // expect the first item to be selected, memory wise it is the max. Assert.assertEquals(this.getExecutorByName("Executor11"), nextExecutor); // add the priority factor. // expect again the #9 item to be selected. comparator.registerComparerForPriority(6); nextExecutor = Collections.max(this.executorList, comparator); Assert.assertEquals(this.getExecutorByName("Executor12"), nextExecutor); // add the remaining space factor. // expect the #12 item to be returned. comparator.registerComparerForRemainingSpace(3); nextExecutor = Collections.max(this.executorList, comparator); Assert.assertEquals(this.getExecutorByName("Executor12"), nextExecutor); } @Test public void testExecutorComparerResisterComparerWInvalidWeight() throws Exception { final MockComparator comparator = new MockComparator(); comparator.registerComparerForMemory(0); } @Test public void testSelector() throws Exception { final MockFilter filter = new MockFilter(); final MockComparator comparator = new MockComparator(); filter.registerFilterforPriority(); filter.registerFilterforRemainingMemory(); filter.registerFilterforRemainingTmpSpace(); filter.registerFilterforTotalMemory(); comparator.registerComparerForMemory(3); comparator.registerComparerForPriority(5); comparator.registerComparerForRemainingSpace(3); final CandidateSelector<MockExecutorObject, MockFlowObject> morkSelector = new CandidateSelector<>( filter, comparator); // mock object, remaining memory 11500, total memory 3095, remainingTmpSpace 4200, priority 2. MockFlowObject dispatchingObj = new MockFlowObject("flow1", 3096, 1500, 4200, 2); // expected selection = #12 MockExecutorObject nextExecutor = morkSelector.getBest(this.executorList, dispatchingObj); Assert.assertEquals(this.getExecutorByName("Executor1"), nextExecutor); // remaining memory 11500, total memory 3095, remainingTmpSpace 14200, priority 2. dispatchingObj = new MockFlowObject("flow1", 3096, 1500, 14200, 2); // all candidates should be filtered by the remaining memory. nextExecutor = morkSelector.getBest(this.executorList, dispatchingObj); Assert.assertEquals(null, nextExecutor); } @Test public void testSelectorsignleCandidate() throws Exception { final MockFilter filter = new MockFilter(); final MockComparator comparator = new MockComparator(); filter.registerFilterforPriority(); filter.registerFilterforRemainingMemory(); filter.registerFilterforRemainingTmpSpace(); filter.registerFilterforTotalMemory(); comparator.registerComparerForMemory(3); comparator.registerComparerForPriority(4); comparator.registerComparerForRemainingSpace(1); final CandidateSelector<MockExecutorObject, MockFlowObject> morkSelector = new CandidateSelector<>( filter, comparator); final ArrayList<MockExecutorObject> signleExecutorList = new ArrayList<>(); final MockExecutorObject signleExecutor = new MockExecutorObject("ExecutorX", 8080, 50.0, 2048, 3, new Date(), 20, 6400); signleExecutorList.add(signleExecutor); final MockFlowObject dispatchingObj = new MockFlowObject("flow1", 100, 100, 100, 5); MockExecutorObject executor = morkSelector.getBest(signleExecutorList, dispatchingObj); // expected to see null result, as the only executor is filtered out . Assert.assertTrue(null == executor); // adjust the priority to let the executor pass the filter. dispatchingObj.priority = 3; executor = morkSelector.getBest(signleExecutorList, dispatchingObj); Assert.assertEquals(signleExecutor, executor); } @Test public void testSelectorListWithItemsThatAreReferenceEqual() throws Exception { final MockFilter filter = new MockFilter(); final MockComparator comparator = new MockComparator(); filter.registerFilterforPriority(); filter.registerFilterforRemainingMemory(); filter.registerFilterforRemainingTmpSpace(); filter.registerFilterforTotalMemory(); comparator.registerComparerForMemory(3); comparator.registerComparerForPriority(4); comparator.registerComparerForRemainingSpace(1); final CandidateSelector<MockExecutorObject, MockFlowObject> morkSelector = new CandidateSelector<>( filter, comparator); final ArrayList<MockExecutorObject> list = new ArrayList<>(); final MockExecutorObject signleExecutor = new MockExecutorObject("ExecutorX", 8080, 50.0, 2048, 3, new Date(), 20, 6400); list.add(signleExecutor); list.add(signleExecutor); final MockFlowObject dispatchingObj = new MockFlowObject("flow1", 100, 100, 100, 3); final MockExecutorObject executor = morkSelector.getBest(list, dispatchingObj); Assert.assertTrue(signleExecutor == executor); } @Test public void testSelectorListWithItemsThatAreEqualInValue() throws Exception { final MockFilter filter = new MockFilter(); final MockComparator comparator = new MockComparator(); filter.registerFilterforPriority(); filter.registerFilterforRemainingMemory(); filter.registerFilterforRemainingTmpSpace(); filter.registerFilterforTotalMemory(); comparator.registerComparerForMemory(3); comparator.registerComparerForPriority(4); comparator.registerComparerForRemainingSpace(1); final CandidateSelector<MockExecutorObject, MockFlowObject> morkSelector = new CandidateSelector<>( filter, comparator); // note - as the tieBreaker set in the MockComparator uses the name value of the executor to do the // final diff therefore we need to set the name differently to make a meaningful test, in real // scenario we may want to use something else (say hash code) to be the bottom line for the tieBreaker // to make a final decision, the purpose of the test here is to prove that for two candidates with // exact value (in the case of test, all values except for the name) the decision result is stable. final ArrayList<MockExecutorObject> list = new ArrayList<>(); final MockExecutorObject executor1 = new MockExecutorObject("ExecutorX", 8080, 50.0, 2048, 3, new Date(), 20, 6400); final MockExecutorObject executor2 = new MockExecutorObject("ExecutorX2", 8080, 50.0, 2048, 3, new Date(), 20, 6400); list.add(executor1); list.add(executor2); final MockFlowObject dispatchingObj = new MockFlowObject("flow1", 100, 100, 100, 3); MockExecutorObject executor = morkSelector.getBest(list, dispatchingObj); Assert.assertTrue(executor2 == executor); // shuffle and test again. list.remove(0); list.add(executor1); executor = morkSelector.getBest(list, dispatchingObj); Assert.assertTrue(executor2 == executor); } @Test public void testSelectorEmptyList() throws Exception { final MockFilter filter = new MockFilter(); final MockComparator comparator = new MockComparator(); filter.registerFilterforPriority(); filter.registerFilterforRemainingMemory(); filter.registerFilterforRemainingTmpSpace(); filter.registerFilterforTotalMemory(); comparator.registerComparerForMemory(3); comparator.registerComparerForPriority(4); comparator.registerComparerForRemainingSpace(1); final CandidateSelector<MockExecutorObject, MockFlowObject> morkSelector = new CandidateSelector<>( filter, comparator); final ArrayList<MockExecutorObject> list = new ArrayList<>(); final MockFlowObject dispatchingObj = new MockFlowObject("flow1", 100, 100, 100, 5); MockExecutorObject executor = null; try { executor = morkSelector.getBest(list, dispatchingObj); } catch (final Exception ex) { Assert.fail("no exception should be thrown when an empty list is passed to the Selector."); } // expected to see null result. Assert.assertTrue(null == executor); try { executor = morkSelector.getBest(list, dispatchingObj); } catch (final Exception ex) { Assert.fail( "no exception should be thrown when null is passed to the Selector as the candidate list."); } // expected to see null result, as the only executor is filtered out . Assert.assertTrue(null == executor); } @Test public void testSelectorListWithNullValue() throws Exception { final MockComparator comparator = new MockComparator(); comparator.registerComparerForMemory(3); comparator.registerComparerForPriority(4); comparator.registerComparerForRemainingSpace(1); final CandidateSelector<MockExecutorObject, MockFlowObject> morkSelector = new CandidateSelector<>( null, comparator); final ArrayList<MockExecutorObject> list = new ArrayList<>(); final MockExecutorObject executor1 = new MockExecutorObject("ExecutorX", 8080, 50.0, 2048, 3, new Date(), 20, 6400); final MockExecutorObject executor2 = new MockExecutorObject("ExecutorX2", 8080, 50.0, 2048, 3, new Date(), 20, 6400); list.add(executor1); list.add(executor2); list.add(null); final MockFlowObject dispatchingObj = new MockFlowObject("flow1", 100, 100, 100, 3); MockExecutorObject executor = null; try { executor = morkSelector.getBest(list, dispatchingObj); } catch (final Exception ex) { Assert.fail("no exception should be thrown when an List contains null value."); } Assert.assertTrue(executor2 == executor); // try to compare null vs null, no exception is expected. list.clear(); list.add(null); list.add(null); try { executor = morkSelector.getBest(list, dispatchingObj); } catch (final Exception ex) { Assert.fail("no exception should be thrown when an List contains multiple null values."); } Assert.assertTrue(null == executor); } @Test public void testCreatingExectorfilterObject() throws Exception { final List<String> validList = new ArrayList<>(ExecutorFilter.getAvailableFilterNames()); try { new ExecutorFilter(validList); } catch (final Exception ex) { Assert.fail( "creating ExecutorFilter with valid list throws exception . ex -" + ex.getMessage()); } } @Test public void testCreatingExectorfilterObjectWInvalidList() throws Exception { final List<String> invalidList = new ArrayList<>(); invalidList.add("notExistingFilter"); Exception result = null; try { new ExecutorFilter(invalidList); } catch (final Exception ex) { if (ex instanceof IllegalArgumentException) { result = ex; } } Assert.assertNotNull(result); } @Test public void testCreatingExectorComparatorObject() throws Exception { final Map<String, Integer> comparatorMap = new HashMap<>(); for (final String name : ExecutorComparator.getAvailableComparatorNames()) { comparatorMap.put(name, 1); } try { new ExecutorComparator(comparatorMap); } catch (final Exception ex) { Assert.fail( "creating ExecutorComparator with valid list throws exception . ex -" + ex.getMessage()); } } @Test public void testCreatingExectorComparatorObjectWInvalidName() throws Exception { final Map<String, Integer> comparatorMap = new HashMap<>(); comparatorMap.put("invalidName", 0); Exception result = null; try { new ExecutorComparator(comparatorMap); } catch (final Exception ex) { if (ex instanceof IllegalArgumentException) { result = ex; } } Assert.assertNotNull(result); } @Test public void testCreatingExectorComparatorObjectWInvalidWeight() throws Exception { final Map<String, Integer> comparatorMap = new HashMap<>(); for (final String name : ExecutorComparator.getAvailableComparatorNames()) { comparatorMap.put(name, -1); } Exception result = null; try { new ExecutorComparator(comparatorMap); } catch (final Exception ex) { if (ex instanceof IllegalArgumentException) { result = ex; } } Assert.assertNotNull(result); } @Test public void testCreatingExecutorSelectorWithEmptyFilterComparatorList() throws Exception { final List<Executor> executorList = new ArrayList<>(); executorList.add(new Executor(1, "host1", 80, true)); executorList.add(new Executor(2, "host2", 80, true)); executorList.add(new Executor(3, "host3", 80, true)); executorList.get(0) .setExecutorInfo(new ExecutorInfo(99.9, 14095, 50, System.currentTimeMillis(), 89, 0)); executorList.get(1) .setExecutorInfo(new ExecutorInfo(50, 14095, 50, System.currentTimeMillis(), 90, 0)); executorList.get(2) .setExecutorInfo(new ExecutorInfo(99.9, 14095, 50, System.currentTimeMillis(), 90, 0)); final ExecutableFlow flow = new ExecutableFlow(); final ExecutorSelector selector = new ExecutorSelector(null, null); final Executor executor = selector.getBest(executorList, flow); Assert.assertEquals(executorList.get(2), executor); } @Test public void testExecutorSelectorE2E() throws Exception { final List<String> filterList = new ArrayList<>(ExecutorFilter.getAvailableFilterNames()); final Map<String, Integer> comparatorMap; comparatorMap = new HashMap<>(); final List<Executor> executorList = new ArrayList<>(); executorList.add(new Executor(1, "host1", 80, true)); executorList.add(new Executor(2, "host2", 80, true)); executorList.add(new Executor(3, "host3", 80, true)); executorList.get(0) .setExecutorInfo(new ExecutorInfo(99.9, 14095, 50, System.currentTimeMillis(), 89, 0)); executorList.get(1) .setExecutorInfo(new ExecutorInfo(50, 14095, 50, System.currentTimeMillis(), 90, 0)); executorList.get(2) .setExecutorInfo(new ExecutorInfo(99.9, 14095, 50, System.currentTimeMillis(), 90, 0)); final ExecutableFlow flow = new ExecutableFlow(); for (final String name : ExecutorComparator.getAvailableComparatorNames()) { comparatorMap.put(name, 1); } final ExecutorSelector selector = new ExecutorSelector(filterList, comparatorMap); Executor executor = selector.getBest(executorList, flow); Assert.assertEquals(executorList.get(0), executor); // simulate that once the flow is assigned, executor1's remaining TMP storage dropped to 2048 // now we do the getBest again executor3 is expected to be selected as it has a earlier last dispatched time. executorList.get(0) .setExecutorInfo(new ExecutorInfo(99.9, 4095, 50, System.currentTimeMillis(), 90, 1)); executor = selector.getBest(executorList, flow); Assert.assertEquals(executorList.get(2), executor); } // mock executor object. static class MockExecutorObject implements Comparable<MockExecutorObject> { public String name; public int port; public double percentOfRemainingMemory; public int amountOfRemainingMemory; public int priority; public Date lastAssigned; public double percentOfRemainingFlowcapacity; public int remainingTmp; public MockExecutorObject(final String name, final int port, final double percentOfRemainingMemory, final int amountOfRemainingMemory, final int priority, final Date lastAssigned, final double percentOfRemainingFlowcapacity, final int remainingTmp) { this.name = name; this.port = port; this.percentOfRemainingMemory = percentOfRemainingMemory; this.amountOfRemainingMemory = amountOfRemainingMemory; this.priority = priority; this.lastAssigned = lastAssigned; this.percentOfRemainingFlowcapacity = percentOfRemainingFlowcapacity; this.remainingTmp = remainingTmp; } @Override public String toString() { return this.name; } @Override public int compareTo(final MockExecutorObject o) { return null == o ? 1 : this.hashCode() - o.hashCode(); } } // Mock flow object. static class MockFlowObject { public String name; public int requiredRemainingMemory; public int requiredTotalMemory; public int requiredRemainingTmpSpace; public int priority; public MockFlowObject(final String name, final int requiredTotalMemory, final int requiredRemainingMemory, final int requiredRemainingTmpSpace, final int priority) { this.name = name; this.requiredTotalMemory = requiredTotalMemory; this.requiredRemainingMemory = requiredRemainingMemory; this.requiredRemainingTmpSpace = requiredRemainingTmpSpace; this.priority = priority; } @Override public String toString() { return this.name; } } // mock Filter class. static class MockFilter extends CandidateFilter<MockExecutorObject, MockFlowObject> { public MockFilter() { } @Override public String getName() { return "Mockfilter"; } // function to register the remainingMemory filter. // for test purpose the registration is put in a separated method, in production the work should be done // in the constructor. public void registerFilterforTotalMemory() { this.registerFactorFilter( FactorFilter.create("requiredTotalMemory", (itemToCheck, sourceObject) -> { // REAL LOGIC COMES HERE - if (null == itemToCheck || null == sourceObject) { return false; } // Box has infinite memory.:) if (itemToCheck.percentOfRemainingMemory == 0) { return true; } // calculate the memory and return. return itemToCheck.amountOfRemainingMemory / itemToCheck.percentOfRemainingMemory * 100 > sourceObject.requiredTotalMemory; })); } public void registerFilterforRemainingMemory() { this.registerFactorFilter( FactorFilter.create("requiredRemainingMemory", (itemToCheck, sourceObject) -> { // REAL LOGIC COMES HERE - if (null == itemToCheck || null == sourceObject) { return false; } return itemToCheck.amountOfRemainingMemory > sourceObject.requiredRemainingMemory; })); } public void registerFilterforPriority() { this.registerFactorFilter( FactorFilter.create("requiredProprity", (itemToCheck, sourceObject) -> { // REAL LOGIC COMES HERE - if (null == itemToCheck || null == sourceObject) { return false; } // priority value, the bigger the lower. return itemToCheck.priority >= sourceObject.priority; })); } public void registerFilterforRemainingTmpSpace() { this.registerFactorFilter( FactorFilter.create("requiredRemainingTmpSpace", (itemToCheck, sourceObject) -> { // REAL LOGIC COMES HERE - if (null == itemToCheck || null == sourceObject) { return false; } return itemToCheck.remainingTmp > sourceObject.requiredRemainingTmpSpace; })); } } // mock comparator class. static class MockComparator extends CandidateComparator<MockExecutorObject> { public MockComparator() { } @Override public String getName() { return "MockComparator"; } @Override protected boolean tieBreak(final MockExecutorObject object1, final MockExecutorObject object2) { if (null == object2) { return true; } if (null == object1) { return false; } return object1.name.compareTo(object2.name) >= 0; } public void registerComparerForMemory(final int weight) { this.registerFactorComparator(FactorComparator.create("Memory", weight, (o1, o2) -> { int result = 0; // check remaining amount of memory. result = o1.amountOfRemainingMemory - o2.amountOfRemainingMemory; if (result != 0) { return result > 0 ? 1 : -1; } // check remaining % . result = (int) (o1.percentOfRemainingMemory - o2.percentOfRemainingMemory); return result == 0 ? 0 : result > 0 ? 1 : -1; })); } public void registerComparerForRemainingSpace(final int weight) { this.registerFactorComparator(FactorComparator.create("RemainingTmp", weight, (o1, o2) -> { int result = 0; // check remaining % . result = (int) (o1.remainingTmp - o2.remainingTmp); return result == 0 ? 0 : result > 0 ? 1 : -1; })); } public void registerComparerForPriority(final int weight) { this.registerFactorComparator(FactorComparator.create("Priority", weight, (o1, o2) -> { int result = 0; // check priority, bigger the better. result = (int) (o1.priority - o2.priority); return result == 0 ? 0 : result > 0 ? 1 : -1; })); } } }