001/** 002 * Copyright 2005-2016 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.rice.xml.ingest; 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 org.apache.commons.lang3.StringUtils; 023import org.kuali.common.util.log.LoggerUtils; 024import org.kuali.common.util.runonce.smart.RunOnce; 025import org.kuali.common.util.runonce.smart.RunOnceState; 026import org.kuali.rice.core.api.util.Truth; 027import org.kuali.rice.coreservice.api.parameter.Parameter; 028import org.kuali.rice.coreservice.api.parameter.ParameterType; 029import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 030import org.kuali.rice.coreservice.framework.parameter.ParameterService; 031import org.slf4j.Logger; 032 033import com.google.common.base.Optional; 034 035/** 036 * Locates workflow XML documents available on the classpath and ingests them. 037 * 038 * @author Kuali Rice Team (rice.collab@kuali.org) 039 */ 040public final class ParameterServiceRunOnce implements RunOnce { 041 042 private static final Logger logger = LoggerUtils.make(); 043 044 private static final String CONFIGURATION_PARAMETER_TYPE = "CONFG"; 045 private static final String YES = "Y"; 046 047 private ParameterService parameterService; 048 private final String applicationId; 049 private final String namespace; 050 private final String component; 051 private final String name; 052 private final Optional<String> description; 053 054 private final boolean runOnMissingParameter; 055 056 private boolean initialized; 057 private boolean runonce; 058 059 /** 060 * {@inheritDoc} 061 */ 062 @Override 063 public synchronized void initialize() { 064 checkState(!initialized, "Already initialized"); 065 066 parameterService = CoreFrameworkServiceLocator.getParameterService(); 067 068 Optional<Parameter> parameter = Optional.fromNullable(parameterService.getParameter(namespace, component, name)); 069 if (!parameter.isPresent() && runOnMissingParameter) { 070 parameter = Optional.of(createParameter()); 071 } 072 runonce = isRunOnce(parameter); 073 showConfig(parameter); 074 075 initialized = true; 076 } 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override 082 public synchronized boolean isTrue() { 083 checkState(initialized, "Not initialized"); 084 085 return runonce; 086 } 087 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 public synchronized void changeState(RunOnceState state) { 093 // Ensure things are as they should be 094 checkState(initialized, "Not initialized"); 095 checkNotNull(state, "'state' cannot be null"); 096 097 // Get the existing parameter 098 Parameter existingParameter = parameterService.getParameter(namespace, component, name); 099 100 // Can't change the state of a non-existent parameter 101 // The isRunOnce() method called during initialization cannot return true unless a parameter exists and it's value is set to 'Y' 102 checkNotNull(existingParameter, "'existingParameter' cannot be null"); 103 104 // Update the parameter 105 logger.info("Updating parameter: [{}]", name); 106 Parameter.Builder builder = Parameter.Builder.create(existingParameter); 107 builder.setValue(state.name()); 108 Parameter updatedParameter = parameterService.updateParameter(builder.build()); 109 110 // This must always return false here 111 runonce = isRunOnce(updatedParameter); 112 checkState(!isTrue(), "isTrue() must return false"); 113 114 // Emit a log message indicating the change in state 115 logger.info("Transitioned RunOnce to - [{}]", updatedParameter.getValue()); 116 } 117 118 private boolean isRunOnce(Optional<Parameter> parameter) { 119 return parameter.isPresent() && isRunOnce(parameter.get()); 120 } 121 122 private boolean isRunOnce(Parameter parameter) { 123 return Truth.strToBooleanIgnoreCase(parameter.getValue(), Boolean.FALSE).booleanValue(); 124 } 125 126 private Parameter createParameter() { 127 logger.info("Creating parameter: [{}]=[{}]", name, YES); 128 ParameterType.Builder parameterTypeBuilder = ParameterType.Builder.create(CONFIGURATION_PARAMETER_TYPE); 129 Parameter.Builder parameterBuilder = Parameter.Builder.create(applicationId, namespace, component, name, parameterTypeBuilder); 130 parameterBuilder.setValue(YES); 131 if (description.isPresent()) { 132 parameterBuilder.setDescription(description.get()); 133 } 134 135 return parameterService.createParameter(parameterBuilder.build()); 136 } 137 138 private void showConfig(Optional<Parameter> optional) { 139 logger.info(String.format("Parameter Metadata: [%s:%s:%s]", applicationId, namespace, component)); 140 if (optional.isPresent()) { 141 Parameter parameter = optional.get(); 142 logger.info("Parameter: [{}]=[{}]", name, parameter.getValue()); 143 } else { 144 logger.info("Parameter [{}] does not exist", name); 145 } 146 logger.info("RunOnce: [{}]", Boolean.valueOf(runonce)); 147 } 148 149 /** 150 * Returns the application identifier of the parameter. 151 * 152 * @return the application identifier of the parameter 153 */ 154 public String getApplicationId() { 155 return applicationId; 156 } 157 158 /** 159 * Returns the namespace of the parameter. 160 * 161 * @return the namespace of the parameter 162 */ 163 public String getNamespace() { 164 return namespace; 165 } 166 167 /** 168 * Returns the component of the parameter. 169 * 170 * @return the component of the parameter 171 */ 172 public String getComponent() { 173 return component; 174 } 175 176 /** 177 * Returns the name of the parameter. 178 * 179 * @return the name of the parameter 180 */ 181 public String getName() { 182 return name; 183 } 184 185 /** 186 * Returns the optional description. 187 * 188 * @return the optional description 189 */ 190 public Optional<String> getDescription() { 191 return description; 192 } 193 194 private ParameterServiceRunOnce(Builder builder) { 195 this.applicationId = builder.applicationId; 196 this.namespace = builder.namespace; 197 this.component = builder.component; 198 this.name = builder.name; 199 this.description = builder.description; 200 this.runOnMissingParameter = builder.runOnMissingParameter; 201 } 202 203 /** 204 * Returns the builder for this {@code ParameterServiceRunOnce}. 205 * 206 * @param applicationId the application identifier of the parameter 207 * @param namespace namespace of the parameter 208 * @param component component of the parameter 209 * @param name name of the parameter 210 * 211 * @return the builder for this {@code ParameterServiceRunOnce} 212 */ 213 public static Builder builder(String applicationId, String namespace, String component, String name) { 214 return new Builder(applicationId, namespace, component, name); 215 } 216 217 /** 218 * Builds this {@link ParameterServiceRunOnce}. 219 */ 220 public static class Builder { 221 222 // Required 223 private String applicationId; 224 private String namespace; 225 private String component; 226 private String name; 227 228 // Optional 229 private Optional<String> description = Optional.absent(); 230 private boolean runOnMissingParameter; 231 232 /** 233 * Builds the {@link ParameterServiceRunOnce}. 234 * 235 * @param applicationId the application identifier of the parameter 236 * @param namespace namespace of the parameter 237 * @param component component of the parameter 238 * @param name name of the parameter 239 */ 240 public Builder(String applicationId, String namespace, String component, String name) { 241 this.applicationId = applicationId; 242 this.namespace = namespace; 243 this.component = component; 244 this.name = name; 245 } 246 247 /** 248 * Sets the description of the parameter. 249 * 250 * @param description the description to set 251 * 252 * @return this {@code Builder} 253 */ 254 public Builder description(String description) { 255 this.description = Optional.fromNullable(description); 256 return this; 257 } 258 259 /** 260 * Sets whether or not to add the parameter if it is missing. 261 * 262 * @param runOnMissingParameter whether or not to add the parameter if it is missing 263 * 264 * @return this {@code Builder} 265 */ 266 public Builder runOnMissingParameter(boolean runOnMissingParameter) { 267 this.runOnMissingParameter = runOnMissingParameter; 268 return this; 269 } 270 271 /** 272 * Builds the {@link ParameterServiceRunOnce}. 273 * 274 * @return the built {@link ParameterServiceRunOnce} 275 */ 276 public ParameterServiceRunOnce build() { 277 ParameterServiceRunOnce instance = new ParameterServiceRunOnce(this); 278 validate(instance); 279 return instance; 280 } 281 282 private static void validate(ParameterServiceRunOnce instance) { 283 checkArgument(!StringUtils.isBlank(instance.getApplicationId()), "'application' id cannot be null"); 284 checkArgument(!StringUtils.isBlank(instance.getNamespace()), "'namespace' cannot be null"); 285 checkArgument(!StringUtils.isBlank(instance.getComponent()), "'component' cannot be null"); 286 checkArgument(!StringUtils.isBlank(instance.getName()), "'name' cannot be null"); 287 checkNotNull(instance.getDescription(), "'description' cannot be null"); 288 } 289 290 } 291 292}