com.radeonsys.data.querystore.support.properties.PropertiesQueryStore.java Source code

Java tutorial

Introduction

Here is the source code for com.radeonsys.data.querystore.support.properties.PropertiesQueryStore.java

Source

/*
 * @(#)PropertiesQueryStore.java            Jun 6, 2012 11:31:30 PM
 *
 * Copyright 2012 Radeon Systems
 *
 *    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.radeonsys.data.querystore.support.properties;

import java.io.IOException;
import java.io.InputStream;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Properties;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
import com.radeonsys.data.querystore.QueryLoadException;
import com.radeonsys.data.querystore.QueryStore;
import com.radeonsys.data.querystore.support.AbstractHashMapQueryStore;
import com.radeonsys.data.querystore.support.loader.ResourceLoader;
import com.radeonsys.data.querystore.support.loader.ResourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An implementation of {@link QueryStore} which supports loading of
 * query definitions from properties resources.
 * 
 * <p>Query definitions can be loaded from a pre-built instance of
 * {@link Properties} object. To use this feature, pass the pre-built
 * {@link Properties} to the {@link #PropertiesQueryStore(Properties)}
 * constructor.</p>
 * 
 * <p>Query definitions can also be loaded from one or more XML properties 
 * file resource using a {@link ResourceLoader}. To use this feature, pass 
 * a list of locations of the XML properties resources and an instance of 
 * {@link ResourceLoader} to be used to load the resource items.</p>
 * 
 * <p>When used with XML properties resources loaded using the 
 * {@link ResourceLoader}, callers must invoke the {@link #initialize()}
 * method before the first call to {@link #getQuery(String)}. The method
 * causes this implementation to read all query definitions off the XML 
 * properties file resource and caches them.</p>
 * 
 * <p>Calling the {@link #initialize()} is not required when using a
 * pre-built {@link Properties} with this implementation.</p>
 * 
 * <p><b>NOTE:</b> This implementation only supports loading XML property
 * resources. It does not support properties files in the single line 
 * format.</p>
 * 
 * @see Properties
 * @see QueryStore
 * @see ResourceLoader
 * 
 * @author oddjobsman
 * @since 1.0
 */
public class PropertiesQueryStore extends AbstractHashMapQueryStore {

    private final Logger logger = LoggerFactory.getLogger(PropertiesQueryStore.class);

    /**
     * Stores a list of all resource locations to be processed
     */
    private List<String> resourceLocations;

    /**
     * Stores a reference to the {@link ResourceLoader} to be used to 
     * load the XML property resources
     */
    private ResourceLoader loaderToUse;

    /**
     * Stores whether or not this query store is initialized
     */
    private boolean storeInitialized = false;

    /**
     * Creates a new instance of {@code PropertiesQueryStore} reading
     * queries off the specified properties
     *  
     * @param props   reference to an instance of {@link Properties} 
     *             containing query definitions
     * 
     * @throws IllegalArgumentException
     *          if the specified properties instance is {@code null}
     */
    public PropertiesQueryStore(Properties props) {
        if (logger.isTraceEnabled())
            logger.trace("Initialized query store with predefined properties file.");

        copyQueriesToStore(props);
        storeInitialized();
    }

    /**
     * Creates a new instance of {@code PropertiesQueryStore} which loads XML
     * properties resources from the specified locations using the
     * specified loader
     * 
     * @param resourceLocations      reference to an instance of {@link List} which contains
     *                         locations of the XML properties resources to load
     * @param loaderToUse         reference to an instance of {@link ResourceLoader} which
     *                         must be used the XML properties resources
     */
    public PropertiesQueryStore(List<String> resourceLocations, ResourceLoader loaderToUse) {
        if (logger.isTraceEnabled())
            logger.trace("Initialized query store from specified resource locations.");

        this.resourceLocations = resourceLocations;
        this.loaderToUse = loaderToUse;
    }

    /**
     * Initializes this query store by loading query definitions from the specified
     * XML properties resources using the specified {@link ResourceLoader} instance
     * 
     * @throws   QueryLoadException
     *          if there was an error loading query definitions from the specified resource locations
     */
    public void initialize() {
        Preconditions.checkState(!storeInitialized, "This query store is already initialized");
        Preconditions.checkState((resourceLocations != null && loaderToUse != null),
                "Cannot initialize query store without specifying resource locations or loader to use");

        processResources();
    }

    /* (non-Javadoc)
     * @see com.radeonsys.data.querystore.QueryStore#getQuery(java.lang.String)
     */
    @Override
    public String getQuery(String nameOfQuery) {
        return getQueryInternal(nameOfQuery);
    }

    /**
     * Process all the resources specified while creating this store
     */
    private void processResources() {
        if (logger.isTraceEnabled())
            logger.trace("Attempting to process resources for queries");

        if (resourceLocations.isEmpty())
            return;

        for (String resourceLocation : resourceLocations)
            copyQueriesToStore(processResource(resourceLocation));

        storeInitialized();
    }

    private void storeInitialized() {
        storeInitialized = true;

        if (logger.isDebugEnabled())
            logger.debug("Initialized query store using properties");
    }

    /**
    * Processes a single resource item to extract the query definitions from the 
    * resource 
    * 
    * @param resourceLocation   the location of the resource item to be processed
    * 
    * @return   reference to an instance of {@link Properties} which contains query
    *          definitions extracted from the resource item
    */
    private Properties processResource(String resourceLocation) {
        if (logger.isTraceEnabled())
            logger.trace(String.format("Processing resource: %s", resourceLocation));

        InputStream is = null;
        final Properties props = new Properties();

        try {
            is = loaderToUse.getBinaryResource(resourceLocation).getInput();
            props.loadFromXML(is);
        } catch (ResourceNotFoundException e) {
            throw new QueryLoadException(String.format("The resource \"%s\" could not be found.", resourceLocation),
                    e);
        } catch (InvalidPropertiesFormatException e) {
            if (logger.isWarnEnabled())
                logger.warn(String.format("Encountered Invalid XML properties format in resource: %s",
                        resourceLocation), e);

            throw new QueryLoadException(String.format(
                    "The resource \"%s\" does not contain a valid XML properties data.", resourceLocation), e);
        } catch (IOException e) {
            if (logger.isWarnEnabled())
                logger.warn(String.format("Encountered IO error while reading resource: %s", resourceLocation), e);

            throw new QueryLoadException(
                    String.format("The resource \"%s\" could not be loaded due to an unexpected I/O error.",
                            resourceLocation),
                    e);
        } finally {
            Closeables.closeQuietly(is);
        }

        if (logger.isTraceEnabled())
            logger.trace(String.format("Loaded %d queries from resource: %s", props.size(), resourceLocation));

        return props;
    }

    /**
     * Copies queries from the specified properties object into this query
     * store
     * 
     * @param props   reference to an instance of {@link Properties} which
     *             contains query definitions to copy
     * 
     * @throws IllegalArgumentException
     *          if the specified properties is {@code null}
     */
    private final void copyQueriesToStore(Properties props) {
        Preconditions.checkArgument(props != null, "A valid set of properties must be specified.");

        if (props.isEmpty())
            return;

        ImmutableMap<String, String> propMap = Maps.fromProperties(props);

        for (String queryName : propMap.keySet())
            addQuery(queryName, propMap.get(queryName).trim());

        if (logger.isTraceEnabled())
            logger.trace(String.format("Copied %d queries into query store", props.size()));
    }

}