001    /**
002     * Copyright 2010-2012 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    
036            @Override
037            public void execute() {
038                    Assert.notNull(propertiesFile);
039                    Assert.notNull(property);
040                    Assert.notNull(executable);
041    
042                    if (!propertiesFile.exists()) {
043                            logger.info("Skipping execution. File does not exist - [{}]", LocationUtils.getCanonicalPath(propertiesFile));
044                            return;
045                    }
046    
047                    logger.info("Examining run once property [{}] in [{}]", property, LocationUtils.getCanonicalPath(propertiesFile));
048                    Properties properties = PropertyUtils.load(propertiesFile, encoding);
049                    ExecutionMode mode = getExecutionMode(properties, property);
050                    boolean runonce = isRunOnce(mode);
051                    if (runonce) {
052                            logger.info("{}={}", property, mode);
053                            // Make sure we have the ability to successfully store updated properties back to the file
054                            if (!isAlways(mode)) {
055                                    setState(properties, property, ExecutionMode.INPROGRESS);
056                            }
057                            try {
058                                    // Invoke execute now that we have successfully transitioned things to INPROGRESS
059                                    executable.execute();
060                                    // There is always a chance that the executable finishes correctly and we encounter some kind of
061                                    // issue just storing the properties back to the file. This should be pretty rare considering
062                                    // we were able to successfully store the properties just prior to the executable commencing
063                                    if (!isAlways(mode)) {
064                                            setState(properties, property, ExecutionMode.COMPLETED);
065                                    }
066                            } catch (Exception e) {
067                                    if (!isAlways(mode)) {
068                                            setState(properties, property, ExecutionMode.FAILED);
069                                    }
070                                    throw new IllegalStateException("Unexpected execution error", e);
071                            }
072                    } else {
073                            logger.info("Skipping execution - [{}={}]", property, mode);
074                    }
075            }
076    
077            protected boolean isAlways(ExecutionMode mode) {
078                    return ExecutionMode.ALWAYS.equals(mode);
079            }
080    
081            protected boolean isRunOnce(ExecutionMode mode) {
082                    if (ExecutionMode.RUNONCE.equals(mode)) {
083                            return true;
084                    }
085                    if (isAlways(mode)) {
086                            return true;
087                    }
088                    return ExecutionMode.TRUE.equals(mode);
089            }
090    
091            protected ExecutionMode getExecutionMode(Properties properties, String key) {
092                    String value = properties.getProperty(property);
093                    if (value == null) {
094                            return ExecutionMode.NULL;
095                    } else {
096                            return ExecutionMode.valueOf(value.toUpperCase());
097                    }
098    
099            }
100    
101            protected void setState(Properties properties, String key, ExecutionMode mode) {
102                    logger.info("{}={}", key, mode);
103                    properties.setProperty(property, mode.name());
104                    PropertyUtils.store(properties, propertiesFile, encoding);
105            }
106    
107            public Executable getExecutable() {
108                    return executable;
109            }
110    
111            public void setExecutable(Executable executable) {
112                    this.executable = executable;
113            }
114    
115            public File getPropertiesFile() {
116                    return propertiesFile;
117            }
118    
119            public void setPropertiesFile(File propertiesFile) {
120                    this.propertiesFile = propertiesFile;
121            }
122    
123            public String getProperty() {
124                    return property;
125            }
126    
127            public void setProperty(String property) {
128                    this.property = property;
129            }
130    
131            public String getEncoding() {
132                    return encoding;
133            }
134    
135            public void setEncoding(String encoding) {
136                    this.encoding = encoding;
137            }
138    }