001 /** 002 * Copyright 2010-2013 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 */ 016 package org.kuali.common.util.execute; 017 018 import java.io.File; 019 import java.util.Properties; 020 021 import org.kuali.common.util.LocationUtils; 022 import org.kuali.common.util.PropertyUtils; 023 import org.slf4j.Logger; 024 import org.slf4j.LoggerFactory; 025 import org.springframework.util.Assert; 026 027 public class RunOnceExecutable implements Executable { 028 029 private static final Logger logger = LoggerFactory.getLogger(RunOnceExecutable.class); 030 031 Executable executable; 032 File propertiesFile; 033 String property; 034 String encoding; 035 boolean skip; 036 037 @Override 038 public void execute() { 039 if (skip) { 040 logger.info("Skipping execution"); 041 return; 042 } 043 Assert.notNull(propertiesFile); 044 Assert.notNull(property); 045 Assert.notNull(executable); 046 047 if (!propertiesFile.exists()) { 048 logger.info("Skipping execution. File does not exist - [{}]", LocationUtils.getCanonicalPath(propertiesFile)); 049 return; 050 } 051 052 logger.info("Examining run once property [{}] in [{}]", property, LocationUtils.getCanonicalPath(propertiesFile)); 053 054 // Load the properties 055 Properties properties = PropertyUtils.load(propertiesFile, encoding); 056 057 // Translate the property value into an execution mode 058 ExecutionMode mode = getExecutionMode(properties, property); 059 060 // Are we going to run once? 061 if (!isRunOnce(mode)) { 062 logger.info("Skipping execution - [{}={}]", property, mode); 063 return; 064 } 065 066 // Show the execution mode we are in 067 logger.info("{}={}", property, mode); 068 069 // Make sure we have the ability to successfully store updated properties back to the file 070 if (!isAlways(mode)) { 071 setState(properties, property, ExecutionMode.INPROGRESS); 072 } 073 074 try { 075 // Invoke execute now that we have successfully transitioned things to INPROGRESS 076 executable.execute(); 077 // There is always a chance that the executable finishes correctly and we encounter some kind of 078 // issue just storing the properties back to the file. This should be pretty rare considering 079 // we were able to successfully store the properties just prior to the executable commencing. 080 // In any event, the executable won't run again in the normal use case because we can only get to this point 081 // if the original execution mode was "TRUE" and we were able to successfully change it to "INPROGRESS" 082 if (!isAlways(mode)) { 083 setState(properties, property, ExecutionMode.COMPLETED); 084 } 085 } catch (Exception e) { 086 if (!isAlways(mode)) { 087 setState(properties, property, ExecutionMode.FAILED); 088 } 089 throw new IllegalStateException("Unexpected execution error", e); 090 } 091 } 092 093 protected boolean isAlways(ExecutionMode mode) { 094 return ExecutionMode.ALWAYS.equals(mode); 095 } 096 097 protected boolean isRunOnce(ExecutionMode mode) { 098 if (ExecutionMode.RUNONCE.equals(mode)) { 099 return true; 100 } 101 if (isAlways(mode)) { 102 return true; 103 } 104 return ExecutionMode.TRUE.equals(mode); 105 } 106 107 protected ExecutionMode getExecutionMode(Properties properties, String key) { 108 String value = properties.getProperty(property); 109 if (value == null) { 110 return ExecutionMode.NULL; 111 } else { 112 return ExecutionMode.valueOf(value.toUpperCase()); 113 } 114 115 } 116 117 protected void setState(Properties properties, String key, ExecutionMode mode) { 118 logger.info("{}={}", key, mode); 119 properties.setProperty(property, mode.name()); 120 PropertyUtils.store(properties, propertiesFile, encoding); 121 } 122 123 public Executable getExecutable() { 124 return executable; 125 } 126 127 public void setExecutable(Executable executable) { 128 this.executable = executable; 129 } 130 131 public File getPropertiesFile() { 132 return propertiesFile; 133 } 134 135 public void setPropertiesFile(File propertiesFile) { 136 this.propertiesFile = propertiesFile; 137 } 138 139 public String getProperty() { 140 return property; 141 } 142 143 public void setProperty(String property) { 144 this.property = property; 145 } 146 147 public String getEncoding() { 148 return encoding; 149 } 150 151 public void setEncoding(String encoding) { 152 this.encoding = encoding; 153 } 154 155 public boolean isSkip() { 156 return skip; 157 } 158 159 public void setSkip(boolean skip) { 160 this.skip = skip; 161 } 162 }