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    }