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 static com.google.common.base.Preconditions.checkArgument; 019import static com.google.common.base.Preconditions.checkNotNull; 020import static com.google.common.base.Preconditions.checkState; 021 022import java.io.File; 023import java.util.Properties; 024 025import org.apache.commons.lang3.StringUtils; 026import org.kuali.common.util.PropertyUtils; 027import org.kuali.common.util.file.CanonicalFile; 028import org.kuali.common.util.log.LoggerUtils; 029import org.slf4j.Logger; 030 031public final class PropertiesFileRunOnce implements RunOnce { 032 033 private static final Logger logger = LoggerUtils.make(); 034 035 private final File file; 036 private final String encoding; 037 private final String key; 038 039 private Properties properties; 040 private boolean runonce; 041 private boolean initialized = false; 042 043 @Override 044 public synchronized void initialize() { 045 checkState(!initialized, "Already initialized"); 046 logger.info("--- Initializing properties file backed RunOnce ---"); 047 this.properties = getProperties(); 048 this.runonce = getBoolean(properties, key); 049 showConfig(); 050 logger.info("--- Properties file backed RunOnce initialized. ---"); 051 this.initialized = true; 052 } 053 054 @Override 055 public synchronized boolean isTrue() { 056 checkState(initialized, "Not initialized"); 057 return runonce; 058 } 059 060 @Override 061 public synchronized void changeState(RunOnceState state) { 062 checkState(initialized, "Not initialized"); 063 checkNotNull(state, "'state' cannot be null"); 064 properties.setProperty(key, state.name()); 065 PropertyUtils.store(properties, file, encoding); 066 this.properties = PropertyUtils.load(file, encoding); 067 this.runonce = getBoolean(properties, key); 068 checkState(!isTrue(), "isTrue() must return false"); 069 logger.info("Transitioned RunOnce to - [{}]", state.name()); 070 } 071 072 private boolean getBoolean(Properties properties, String key) { 073 String value = properties.getProperty(key); 074 return Boolean.parseBoolean(value); 075 } 076 077 protected void showConfig() { 078 logger.info("Properties file: [{}]", file); 079 logger.info("Properties file exists: {}", file.exists()); 080 logger.info("Property: [{}]=[{}]", key, properties.get(key)); 081 logger.info("RunOnce: [{}]", runonce); 082 } 083 084 protected Properties getProperties() { 085 if (file.exists()) { 086 return PropertyUtils.load(file, encoding); 087 } else { 088 return new Properties(); 089 } 090 } 091 092 private PropertiesFileRunOnce(Builder builder) { 093 this.file = builder.file; 094 this.encoding = builder.encoding; 095 this.key = builder.key; 096 } 097 098 public static Builder builder(File file, String encoding, String key) { 099 return new Builder(file, encoding, key); 100 } 101 102 public static class Builder { 103 104 private final File file; 105 private final String key; 106 private final String encoding; 107 108 public Builder(File file, String encoding, String key) { 109 this.file = new CanonicalFile(file); 110 this.encoding = encoding; 111 this.key = key; 112 } 113 114 public PropertiesFileRunOnce build() { 115 PropertiesFileRunOnce instance = new PropertiesFileRunOnce(this); 116 validate(instance); 117 return instance; 118 } 119 120 private void validate(PropertiesFileRunOnce instance) { 121 checkNotNull(instance.getFile(), "file cannot be null"); 122 checkArgument(!StringUtils.isBlank(instance.getEncoding()), "encoding cannot be blank"); 123 checkArgument(!StringUtils.isBlank(instance.getKey()), "key cannot be blank"); 124 } 125 } 126 127 public File getFile() { 128 return file; 129 } 130 131 public String getEncoding() { 132 return encoding; 133 } 134 135 public String getKey() { 136 return key; 137 } 138 139}