001/**
002 * Copyright 2005-2018 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}