Java tutorial
/****************************************************************************** * NOTICE * * * * This software (or technical data) was produced for the U.S. Government * * under contract, and is subject to the Rights in Data-General Clause * * 52.227-14, Alt. IV (DEC 2007). * * * * Copyright 2018 The MITRE Corporation. All Rights Reserved. * ******************************************************************************/ /****************************************************************************** * Copyright 2018 The MITRE Corporation * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * ******************************************************************************/ package org.mitre.mpf.wfm.util; import org.apache.commons.configuration2.*; import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; import org.apache.commons.configuration2.builder.fluent.Parameters; import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler; import org.apache.commons.configuration2.ex.ConfigurationException; import org.mitre.mpf.mvc.model.PropertyModel; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import java.io.IOException; import java.net.URL; import java.util.*; @Component public class MpfPropertiesConfigurationBuilder { public static final String DETECTION_KEY_PREFIX = "detection."; @javax.annotation.Resource(name = "customPropFile") private FileSystemResource customPropFile; public void setCustomPropFile(FileSystemResource customPropFile) { this.customPropFile = customPropFile; } @javax.annotation.Resource(name = "propFiles") private List<Resource> propFiles; public void setPropFiles(List<Resource> propFiles) { this.propFiles = propFiles; } private CompositeConfiguration mpfCompositeConfig; // return a snapshot to users of this class to prevent the case where a property is being set / updated at the same // time that properties are being read out of the config // this is volatile to ensure the most updated version is used across threads private volatile PropertiesConfiguration mpfConfigSnapshot; private PropertiesConfiguration mpfCustomPropertiesConfig; public ImmutableConfiguration getCompleteConfiguration() { if (mpfConfigSnapshot == null) { mpfCompositeConfig = createCompositeConfiguration(); updateConfigurationSnapshot(); } return mpfConfigSnapshot; } public synchronized ImmutableConfiguration setAndSaveCustomProperties(List<PropertyModel> propertyModels) { // create a new builder and configuration to write the properties to disk // without affecting the values of the composite config FileBasedConfigurationBuilder<PropertiesConfiguration> tmpMpfCustomPropertiesConfigBuilder = createFileBasedConfigurationBuilder( customPropFile); Configuration tmpMpfCustomPropertiesConfig; try { tmpMpfCustomPropertiesConfig = tmpMpfCustomPropertiesConfigBuilder.getConfiguration(); } catch (ConfigurationException e) { throw new IllegalStateException("Cannot create configuration from " + customPropFile + ".", e); } for (PropertyModel propModel : propertyModels) { String key = propModel.getKey(); String value = propModel.getValue(); // update all properties that will be written to disk tmpMpfCustomPropertiesConfig.setProperty(key, value); // update only the detection.* values in the composite config used by the WFM if (key.startsWith(DETECTION_KEY_PREFIX)) { mpfCustomPropertiesConfig.setProperty(key, value); } } try { tmpMpfCustomPropertiesConfigBuilder.save(); } catch (ConfigurationException e) { throw new IllegalStateException("Cannot save configuration to " + customPropFile + ".", e); } updateConfigurationSnapshot(); return mpfConfigSnapshot; } public synchronized List<PropertyModel> getCustomProperties() { // create a new builder and configuration to read the properties on disk FileBasedConfigurationBuilder<PropertiesConfiguration> tmpMpfCustomPropertiesConfigBuilder = createFileBasedConfigurationBuilder( customPropFile); Configuration tmpMpfCustomPropertiesConfig; try { tmpMpfCustomPropertiesConfig = tmpMpfCustomPropertiesConfigBuilder.getConfiguration(); } catch (ConfigurationException e) { throw new IllegalStateException("Cannot create configuration from " + customPropFile + ".", e); } // generate a complete list of property models and determine if a WFM restart is needed for each List<PropertyModel> propertyModels = new ArrayList<>(); Iterator<String> propertyKeyIter = mpfCompositeConfig.getKeys(); while (propertyKeyIter.hasNext()) { String key = propertyKeyIter.next(); // use uninterpolated values String currentValue = getRawValue(mpfCompositeConfig, key); if (tmpMpfCustomPropertiesConfig.containsKey(key)) { String customValue = getRawValue(tmpMpfCustomPropertiesConfig, key); boolean needsRestart = !Objects.equals(currentValue, customValue); propertyModels.add(new PropertyModel(key, customValue, needsRestart)); } else { propertyModels.add(new PropertyModel(key, currentValue, false)); } } return propertyModels; } private String getRawValue(Configuration config, String key) { Object prop = config.getProperty(key); String raw = prop.toString(); if (prop instanceof Collection<?>) { return raw.substring(1, raw.length() - 1); // remove the beginning "[" and ending "]" } return raw; } private CompositeConfiguration createCompositeConfiguration() { if (!customPropFile.exists()) { try { PropertiesUtil.createParentDir(customPropFile); customPropFile.getFile().createNewFile(); } catch (IOException e) { throw new IllegalStateException("Cannot create " + customPropFile + ".", e); } } CompositeConfiguration compositeConfig = new CompositeConfiguration(); // add resources in the order they are specified in the application context XML; // the first configs that are added to the composite override property values in configs that are added later for (Resource resource : propFiles) { try { if (resource.equals(customPropFile)) { mpfCustomPropertiesConfig = createFileBasedConfigurationBuilder(customPropFile) .getConfiguration(); compositeConfig.addConfiguration(mpfCustomPropertiesConfig); } else if (resource.exists()) { compositeConfig .addConfiguration(createFileBasedConfigurationBuilder(resource).getConfiguration()); } } catch (ConfigurationException e) { throw new IllegalStateException("Cannot create configuration from " + resource + ".", e); } } if (mpfCustomPropertiesConfig == null) { throw new IllegalStateException("List of configuration properties files did not contain the " + "custom configuration property file: " + propFiles); } return compositeConfig; } private FileBasedConfigurationBuilder<PropertiesConfiguration> createFileBasedConfigurationBuilder( Resource resource) { URL url; try { url = resource.getURL(); } catch (IOException e) { throw new IllegalStateException("Cannot get URL from " + resource + ".", e); } FileBasedConfigurationBuilder<PropertiesConfiguration> fileBasedConfigBuilder = new FileBasedConfigurationBuilder<>( PropertiesConfiguration.class); Parameters configBuilderParameters = new Parameters(); fileBasedConfigBuilder.configure(configBuilderParameters.fileBased().setURL(url) .setListDelimiterHandler(new DefaultListDelimiterHandler(','))); return fileBasedConfigBuilder; } private void updateConfigurationSnapshot() { PropertiesConfiguration tmpConfig = new PropertiesConfiguration(); // this will copy over each property one at a time, // essentially generating a "flat" config from the composite config ConfigurationUtils.copy(mpfCompositeConfig, tmpConfig); mpfConfigSnapshot = tmpConfig; } }