/******************************************************************************* * Copyright 2017 Capital One Services, LLC and Bitwise, 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 hydrograph.ui.expression.editor.sourceviewer; public abstract class ExecutionLimiter { private boolean inExecution; private long timeBeforeNewExecution; private long startTime; private boolean finalExecute; private FinalExecution finalExecution; private Thread executeAtEndOfTimeThread; private FinalExecution finalExecutionThreadWait; public ExecutionLimiter() { super(); } public ExecutionLimiter(long timeBeforeNewExecute) { this.timeBeforeNewExecution = timeBeforeNewExecute; } /** * * DOC amaumont ExecutionLimiter constructor comment. * * @param timeBeforeNewExecute time max between executions * @param finalExecute execute at end of time the treatment to ensure it is executed a least one time after last * call of startIfExecutable() */ public ExecutionLimiter(long timeBeforeNewExecute, boolean finalExecute) { this.timeBeforeNewExecution = timeBeforeNewExecute; this.finalExecute = finalExecute; } public boolean startIfExecutable() { return startIfExecutable(false, null); } public boolean startIfExecutable(Object data) { return startIfExecutable(false, data); } /** * Start execution if executable, after <code>timeBeforeNewExecute</code> is elapsed if * <code>executeAtEndOfTime</code> is true. * * @param executeAtEndOfTime if true call <code>execute()</code> now, else call <code>execute()</code> at end of * <code>timeBeforeNewExecute</code> * @param data TODO * @return true if executable, false else */ public boolean startIfExecutable(boolean executeAtEndOfTime, final Object data) { boolean executable = false; executable = isExecutable(executeAtEndOfTime); if (executable) { inExecution = true; if (executeAtEndOfTime) { (new Thread() { @Override public void run() { try { // System.out.println("1 HASHCODE = " + ExecutionLimiter.this.hashCode() + " " + // this.hashCode()); // Thread.sleep(timeBeforeNewExecution); synchronized (this) { // System.out.println("2 HASHCODE = " + ExecutionLimiter.this.hashCode() + " " + // this.hashCode()); executeAtEndOfTimeThread = this; this.wait(timeBeforeNewExecution); } // System.out.println("Call executed: executeAtEndOfTime" + ExecutionLimiter.this.hashCode() // + " " + this.hashCode()); callExecute(data); } catch (InterruptedException e) { // System.out.println("=======> executeAtEndOfTime interrupted" + // ExecutionLimiter.this.hashCode() + " " + this.hashCode()); return; } catch (Exception e) { throw new RuntimeException(e); } finally { inExecution = false; } } }).start(); } else { // //System.out.println( "Call executed : now"); callExecute(data); } } else { // //System.out.println( "Call rejected"); } if (finalExecute) { // System.out.println("startThreadForFinalExecution();"); startThreadForFinalExecution(data); } if (executable && !executeAtEndOfTime) { inExecution = false; } return executable; } private void callExecute(Object data) { startTime = System.currentTimeMillis(); execute(false, data); } /** * DOC amaumont Comment method "startThreadForFinalExecution". * @param data */ private void startThreadForFinalExecution(final Object data) { (new Thread() { @Override public void run() { FinalExecution finalThread = new FinalExecution(data); if (finalExecutionThreadWait != null && !finalExecutionThreadWait.isInterrupted()) { finalExecutionThreadWait.interrupt(); } finalExecutionThreadWait = finalThread; finalThread.start(); } }).start(); } /** * * DOC amaumont ExecutionLimiter class global comment. Detailled comment <br/> * */ class FinalExecution extends Thread { private Object data; public FinalExecution(Object data) { this.data = data; } public void run() { try { synchronized (this) { // finalExecutionThreadWait = this; this.wait(timeBeforeNewExecution); } // Thread.sleep(timeBeforeNewExecution); } catch (InterruptedException e) { // System.out.println("FinalExecution Interrupted " + ExecutionLimiter.this.hashCode() + " " + // this.hashCode()); return; } // System.out.println("FinalExecution Not Interrupted " + ExecutionLimiter.this.hashCode() + " " + // this.hashCode()); // System.out.println("Final thread executed"); execute(true, data); } } /** * * DOC amaumont Comment method "execute". * @param isFinalExecution * @param data can be null */ protected abstract void execute(boolean isFinalExecution, Object data); private boolean isExecutable(boolean executeAtEndOfTime) { boolean returnValue = false; if (executeAtEndOfTime) { returnValue = !inExecution; } else { if (timeBeforeNewExecution == 0) { returnValue = !inExecution; } else { returnValue = System.currentTimeMillis() - startTime >= timeBeforeNewExecution; // System.out.println(System.currentTimeMillis() - startTime + " >= " + timeBeforeNewExecution + " " + // returnValue); } } return returnValue; } public long getTimeBeforeNewExecution() { return timeBeforeNewExecution; } public void setTimeBeforeNewExecution(long timeBeforeNewExecute) { this.timeBeforeNewExecution = timeBeforeNewExecute; } public void resetTimer() { // System.out.println("############### RESET timer"); startTime = System.currentTimeMillis(); if (executeAtEndOfTimeThread != null && !executeAtEndOfTimeThread.isInterrupted()) { executeAtEndOfTimeThread.interrupt(); } if (finalExecutionThreadWait != null && !finalExecutionThreadWait.isInterrupted()) { finalExecutionThreadWait.interrupt(); } } }