001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.runonce.smart; 017 018import org.kuali.common.util.execute.Executable; 019import org.kuali.common.util.log.LoggerUtils; 020import org.slf4j.Logger; 021 022import com.google.common.base.Preconditions; 023 024public class RunOnceExecutable implements Executable { 025 026 private static final Logger logger = LoggerUtils.make(); 027 028 private final Executable executable; 029 private final RunOnce runOnce; 030 private final boolean skip; 031 032 @Override 033 public void execute() { 034 035 // Skip has been explicitly configured 036 if (skip) { 037 logger.info("Skipping RunOnce"); 038 return; 039 } 040 041 // Give the RunOnce logic a chance to initialize itself 042 runOnce.initialize(); 043 044 // If run once is not enabled, we are done 045 if (!runOnce.isTrue()) { 046 return; 047 } 048 049 // Transition to INPROGRESS 050 runOnce.changeState(RunOnceState.INPROGRESS); 051 052 // Make sure the run once indicator no longer returns true 053 Preconditions.checkState(!runOnce.isTrue(), "Run once must be false"); 054 055 try { 056 // Now that we have transitioned things to INPROGRESS and verified that the RunOnce indicator 057 // no longer returns true, it is safe to fire the executable 058 // This sequence of events is what prevents us from running the executable more than once 059 executable.execute(); 060 061 // Transition to COMPLETED 062 runOnce.changeState(RunOnceState.COMPLETED); 063 } catch (Exception e) { 064 // Transition to FAILED 065 runOnce.changeState(RunOnceState.FAILED); 066 throw new IllegalStateException("Unexpected execution error", e); 067 } 068 } 069 070 public static Builder builder(Executable executable, RunOnce runOnce) { 071 return new Builder(executable, runOnce); 072 } 073 074 public static class Builder { 075 076 // Required 077 private final Executable executable; 078 private final RunOnce runOnce; 079 080 // Optional 081 private boolean skip = false; 082 083 public Builder(Executable executable, RunOnce runOnce) { 084 this.executable = executable; 085 this.runOnce = runOnce; 086 } 087 088 public Builder skip(boolean skip) { 089 this.skip = skip; 090 return this; 091 } 092 093 public RunOnceExecutable build() { 094 RunOnceExecutable instance = new RunOnceExecutable(this); 095 validate(instance); 096 return instance; 097 } 098 099 private void validate(RunOnceExecutable instance) { 100 Preconditions.checkNotNull(instance.getExecutable(), "executable cannot be null"); 101 Preconditions.checkNotNull(instance.getRunOnce(), "runOnce cannot be null"); 102 } 103 } 104 105 private RunOnceExecutable(Builder builder) { 106 this.executable = builder.executable; 107 this.runOnce = builder.runOnce; 108 this.skip = builder.skip; 109 } 110 111 public Executable getExecutable() { 112 return executable; 113 } 114 115 public boolean isSkip() { 116 return skip; 117 } 118 119 public RunOnce getRunOnce() { 120 return runOnce; 121 } 122 123}