All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.likeness.mojo.numbers.PropertyCache Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2011 Ness Computing, Inc.
 *
 * 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 com.likeness.mojo.numbers;

import static java.lang.String.format;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.Properties;

import javax.annotation.Nonnull;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
import com.likeness.mojo.numbers.beans.AbstractDefinition;
import com.likeness.mojo.numbers.beans.IWFCEnum;
import com.likeness.mojo.numbers.util.Log;

public class PropertyCache
{
    private static final Log LOG = Log.findLog();

    /** Cache for properties files loaded from disk */
    private Map propFiles = Maps.newHashMap();

    private final Properties ephemeralProperties = new Properties();

    public ValueProvider getPropertyValue(final AbstractDefinition definition)
        throws IOException
    {
        final Properties props = getProperties(definition);
        if (props == null) {
            final String propName = definition.getPropertyName();
            final String value = definition.getInitialValue();
            if (value != null) {
                ephemeralProperties.setProperty(propName, value);
            }
            return new ValueProvider.PropertyProvider(ephemeralProperties, propName);
        }
        else {
            return findCurrentValue(props, definition);
        }
    }

    @VisibleForTesting
    ValueProvider findCurrentValue(final Properties props, final AbstractDefinition definition)
    {
        final String propName = definition.getPropertyName();
        final boolean hasProperty = props.containsKey(propName);

        final boolean createProperty = IWFCEnum.checkState(definition.getOnMissingProperty(), hasProperty, propName);

        if (hasProperty) {
            return new ValueProvider.PropertyProvider(props, propName);
        }
        else if (createProperty) {
            props.setProperty(propName, definition.getInitialValue());
            return new ValueProvider.PropertyProvider(props, propName);
        }
        else {
            return ValueProvider.NULL_PROVIDER;
        }
    }

    @VisibleForTesting
    Properties getProperties(final AbstractDefinition definition)
        throws IOException
    {
        final File definitionPropertyFile = definition.getPropertyFile();

        // Ephemeral, so return null.
        if (definitionPropertyFile == null) {
            return null;
        }

        PropertyCacheEntry propertyCacheEntry;
        final File propertyFile = definitionPropertyFile.getCanonicalFile();

        // Throws an exception if the file must exist and does not.
        final boolean createFile = IWFCEnum.checkState(definition.getOnMissingFile(), propertyFile.exists(), definitionPropertyFile.getCanonicalPath());

        propertyCacheEntry = propFiles.get(propertyFile);

        if (propertyCacheEntry != null) {
            // If there is a cache hit, something either has loaded the file
            // or another property has already put in a creation order.
            // Make sure that if this number has a creation order it is obeyed.
            if (createFile) {
                propertyCacheEntry.doCreate();
            }
        }
        else {
            // Try loading or creating properties.
            final Properties props = new Properties();

            if (!propertyFile.exists()) {
                propertyCacheEntry = new PropertyCacheEntry(props, false, createFile); // does not exist
            }
            else {
                if (propertyFile.isFile() && propertyFile.canRead()) {
                    InputStream stream = null;
                    try {
                        stream = new FileInputStream(propertyFile);
                        props.load(stream);
                        propertyCacheEntry = new PropertyCacheEntry(props, true, createFile);
                        propFiles.put(propertyFile, propertyCacheEntry);
                    }
                    finally {
                        Closeables.closeQuietly(stream);
                    }
                }
                else {
                    throw new IllegalStateException(format("Can not load %s, not a file!", definitionPropertyFile.getCanonicalPath()));
                }
            }
        }

        return propertyCacheEntry.getProps();
    }

    public void persist() throws IOException
    {
        for (final Map.Entry propFile : propFiles.entrySet())
        {
            final PropertyCacheEntry entry = propFile.getValue();
            final File file = propFile.getKey();
            if (entry.isExists() || entry.isCreate()) {
                Preconditions.checkNotNull(file, "no file defined, can not persist!");
                final File oldFile = new File(file.getCanonicalPath() + ".bak");

                if (entry.isExists()) {
                    Preconditions.checkState(file.exists(), "File %s should exist!", file.getCanonicalPath());
                    // unlink an old file if necessary
                    if (oldFile.exists()) {
                        Preconditions.checkState(oldFile.delete(), "Could not delete '%s'", file.getCanonicalPath());
                    }
                }
                final File newFile = new File(file.getCanonicalPath() + ".new");
                OutputStream stream = null;
                try {
                    stream = new FileOutputStream(newFile);
                    entry.getProps().store(stream, "created by maven-numbers-plugin");
                }
                finally {
                    Closeables.closeQuietly(stream);
                }

                if (file.exists()) {
                    if (file.renameTo(oldFile)) {
                        if (!newFile.renameTo(file)) {
                            LOG.warn("Could not rename '%s' to '%s'!", newFile, file);
                        }
                    }
                    else {
                        LOG.warn("Could not rename '%s' to '%s'!", file, oldFile);
                    }
                }
            }
        }
    }

    public static class PropertyCacheEntry
    {
        private final Properties props;

        private final boolean exists;

        private boolean create;

        PropertyCacheEntry(@Nonnull final Properties props,
                               final boolean exists,
                               final boolean create)
        {
            Preconditions.checkNotNull(props, "Properties element can not be null!");
            this.props = props;
            this.exists = exists;
            this.create = create;
        }

        public Properties getProps()
        {
            return props;
        }

        public boolean isExists()
        {
            return exists;
        }

        public boolean isCreate()
        {
            return create;
        }

        public void doCreate()
        {
            this.create = true;
        }

        @Override
        public boolean equals(final Object other)
        {
            if (!(other instanceof PropertyCacheEntry))
                return false;
            PropertyCacheEntry castOther = (PropertyCacheEntry) other;
            return new EqualsBuilder().append(props, castOther.props).append(exists, castOther.exists).append(create, castOther.create).isEquals();
        }

        @Override
        public int hashCode()
        {
            return new HashCodeBuilder().append(props).append(exists).append(create).toHashCode();
        }

        @Override
        public String toString()
        {
            return new ToStringBuilder(this).append("props", props).append("exists", exists).append("create", create).toString();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy