Package com.google.feedserver.samples.configstore

Source Code of com.google.feedserver.samples.configstore.SampleFileSystemFeedConfigStore

/*
* Copyright 2008 Google 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.google.feedserver.samples.configstore;

import com.google.feedserver.config.FeedServerConfiguration;
import com.google.feedserver.config.MixinConfiguration;
import com.google.feedserver.config.NamespacedAdapterConfiguration;
import com.google.feedserver.config.NamespacedFeedConfiguration;
import com.google.feedserver.config.PerNamespaceServerConfiguration;
import com.google.feedserver.configstore.FeedConfigStore;
import com.google.feedserver.configstore.FeedConfigStoreException;
import com.google.feedserver.configstore.FeedConfigStoreException.Reason;
import com.google.feedserver.samples.config.MapMixinConfiguration;
import com.google.feedserver.samples.config.XmlMixinConfiguration;
import com.google.feedserver.util.ConfigStoreUtil;
import com.google.feedserver.util.FileSystemConfigStoreUtil;
import com.google.feedserver.util.FileUtil;

import org.apache.abdera.protocol.server.provider.managed.CollectionAdapterConfiguration;
import org.apache.abdera.protocol.server.provider.managed.FeedConfiguration;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* This is a sample implementation of the {@link FeedConfigStore}. The feed and
* adapter configurations are stored as files on the file system.
* <p>
* Following folder structure is used: <br/>
* <ul>
* <li><Root deployment directory>
* <ul>
* <li>conf
* <ul>
* <li>feedserver<ul><li>FeedConfig<ul><li>'%feedId%'.properties -- The file with feed
* configuration values </li></ul></li></ul> <ul><li>AdapterConfig<ul><li>'%adapterConfigName%'.properties -- The
* file with adapter configuration values</li></ul><li>Adapter<ul><li>'%adapterName%'.properties -- The
* file with adapter values</li></li></ul></li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
* </p>
*
* @author rakeshs101981@gmail.com (Rakesh Shete)
*
*/
public class SampleFileSystemFeedConfigStore implements FeedConfigStore {

  private static final Logger logger =
      Logger.getLogger(SampleFileSystemFeedConfigStore.class.getName());

  /**
   * The placeholder for the namespace directory name which will be replaced by
   * the actual namepsace provided with the request
   */
  private static final String namespacePlaceHolder = "namespace";

  /**
   * The base path of the directory under which the feed and adapter
   * configuration will be stored
   */
  public static String BASE_CONFIGURATION_PATH = "conf/feedserver";

  /**
   * The base path to the feed configuration directory with the namespace
   * placeholder which should be replaced with the actual namespace at runtime
   */
  private static String FEED_CONFIGURATION_PATH =
      BASE_CONFIGURATION_PATH + "/" + namespacePlaceHolder + "/"
          + FeedServerConfiguration.FEED_CONFIGURATION;

  /**
   * The base path to the adapter configuration directory with the namespace
   * placeholder which should be replaced with the actual namespace at runtime
   */
  private static String ADAPTER_CONFIGURATION_PATH =
      BASE_CONFIGURATION_PATH + "/" + namespacePlaceHolder + "/"
          + FeedServerConfiguration.ADAPTER_CONFIGURATION;

  private static String ADAPTER_PATH =
      BASE_CONFIGURATION_PATH + "/" + namespacePlaceHolder + "/" + "Adapter";

  /**
   * The file extension to be used for constructing the file name
   */
  private static String FILE_EXTENSION = ".properties";

  /**
   * The file extension to be used for constructing the file name
   */
  private static String MIXIN_FILE_EXTENSION = ".xml";

  /**
   * The property name identifying the adapter class name
   */
  public static final String ADAPTER_CLASS_NAME = "className";

  protected FileUtil fileUtil;

  /**
   * Default constructor
   */
  public SampleFileSystemFeedConfigStore() {
    fileUtil = new FileUtil();
  }

  /**
   * Stores the feed configuration to the file system as properties file under
   * the given namespace
   *
   * @param namespace The namespace under which the feed configuration has to be
   *        stored
   * @param config The feed configuration
   * @throws FeedConfigStoreException If any exception is encountered while
   *         storing the feed configuration
   *
   * @see com.google.feedserver.configstore.FeedConfigStore#addFeed(String,
   *      FeedConfiguration)
   */
  @Override
  public void addFeed(String namespace, FeedConfiguration config) throws FeedConfigStoreException {
    // Get the feed configuration properties to save to file
    Properties prop = extractFeedConfigProperties(config);

    String adapterName = prop.getProperty(FeedServerConfiguration.FEED_ADAPTER_NAME_KEY);

    if (!hasAdapterConfiguration(namespace, adapterName)) {
      throw new FeedConfigStoreException(Reason.ADAPTER_CONFIG_DOES_NOT_EXIST,
          "There is no adapter configuration with the given name : " + adapterName
              + " Add the adapter configuration and then try adding feed configuration");
    }

    // Check and create the namespace and feed configuration directories if
    // they don't exist
    createNamespaceAndFeedConfigDir(namespace);

    // Get the handle to the file at the given location
    File feedFile = getFeedConfigFilePath(namespace, config.getFeedId());

    // Check that the file does not exist
    if (feedFile.exists()) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR, "The feed already exists");
    }

    // Write the feed values to the file
    writeToFile(prop, feedFile, true);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #getAdapterConfiguration(java.lang.String, java.lang.String)
   */
  @Override
  public CollectionAdapterConfiguration getAdapterConfiguration(String namespace, String adapterName)
      throws FeedConfigStoreException {
    logger.log(Level.FINE, "Retrieving the adapter configuration with given namespace : "
        + namespace + " and adapterName : " + adapterName);

    return getAdapterConfig(namespace, adapterName);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #getAdapterConfigurations(java.lang.String)
   */
  @Override
  public Map<String, CollectionAdapterConfiguration> getAdapterConfigurations(String namespace)
      throws FeedConfigStoreException {
    // Check that the namespace and feed configuration directories exist
    File adapterConfigDir = checkNamespaceAndAdapterConfigDir(namespace);

    Map<String, CollectionAdapterConfiguration> adapterConfigurations =
        new HashMap<String, CollectionAdapterConfiguration>();
    // Traverse all the files, store the filenames (without the extension)
    // as
    // adpaternames and get the adapter configurations for the same
    for (String adapterConfigFileName : adapterConfigDir.list()) {
      String adapterName =
          adapterConfigFileName.substring(0, adapterConfigFileName.lastIndexOf("."));
      NamespacedAdapterConfiguration adapterConfiguration = null;

      try {
        adapterConfiguration = getAdapterConfig(namespace, adapterName);
      } catch (FeedConfigStoreException e) {
        logger.log(Level.SEVERE, "Unable to retrieve adapter configuration with adapterName : "
            + adapterName + "  with the given namespace : " + namespace);
        continue;
      }
      adapterConfigurations.put(adapterName, adapterConfiguration);
    }
    return adapterConfigurations;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #getAllowedAdapters(java.lang.String)
   */
  @Override
  public Collection<String> getAllowedAdapters(String namespace) throws FeedConfigStoreException {
    // Check that the namespace and feed configuration directories exist
    File adapterConfigDir = checkNamespaceAndAdapterConfigDir(namespace);
    List<String> adapterNames = new ArrayList<String>();

    for (String adapterConfigFileName : adapterConfigDir.list()) {
      adapterNames.add(adapterConfigFileName.substring(0, adapterConfigFileName.lastIndexOf(".")));
    }

    Collections.sort(adapterNames);
    return adapterNames;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #getFeedConfiguration(java.lang.String, java.lang.String)
   */
  @Override
  public FeedConfiguration getFeedConfiguration(String namespace, String feedId, String userId)
      throws FeedConfigStoreException {

    logger.log(Level.FINE, "Retrieving feed config: namespace=" + namespace + " feedId=" +
        feedId + " userId=" + userId);

    // Construct the feed file path
    File feedFile = getFeedConfigFilePath(namespace, feedId, userId);

    logger.log(Level.FINEST, "The path of the file to fetch the feed configuration from : "
        + feedFile.getAbsolutePath());

    try {
      // Get the feed configuration
      return getFeedConfiguration(namespace, feedId, feedFile);
    } catch (FileNotFoundException e) {
      throw new FeedConfigStoreException(Reason.FEED_DOES_NOT_EXIST, "File '" +
          feedFile.getAbsolutePath() + "' not found.  Please check namespace '" + namespace +
          "' and feedId '" + feedId + "'");
    } catch (IOException e) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Problems loading the feed configuration for namespace '" + namespace + "' and feedId '"
          + feedId + "'");
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #getFeedConfigurations(java.lang.String)
   */
  @Override
  public Map<String, FeedConfiguration> getFeedConfigurations(String namespace)
      throws FeedConfigStoreException {
    // Check that the namespace and feed configuration directories exist
    File feedConfigDir = checkNamespaceAndFeedConfigDir(namespace);

    Map<String, FeedConfiguration> feedConfigurations = new HashMap<String, FeedConfiguration>();
    // Traverse all the files, store the filenames (without the extension)
    // as feedIds and get the feed configurations for the same
    for (String feedConfigFileName : feedConfigDir.list()) {
      if (feedConfigFileName.endsWith(FILE_EXTENSION)) {
        String feedId = feedConfigFileName.substring(0, feedConfigFileName.lastIndexOf("."));
        File feedConfigFile = getFeedConfigFilePath(namespace, feedId);
        FeedConfiguration feedConfiguration;
        try {
          feedConfiguration = getFeedConfiguration(namespace, feedId, feedConfigFile);
        } catch (FileNotFoundException e) {
          logger.log(Level.WARNING, "Unable to retrieve feed configuration for feedId : " + feedId
              + "  with the given namespace : " + namespace);
          continue;
        } catch (IOException e) {
          logger.log(Level.WARNING, "Unable to retrieve feed configuration for feedId : " + feedId
              + "  with the given namespace : " + namespace);
          continue;
        }

        feedConfigurations.put(feedId, feedConfiguration);
      }
    }
    return feedConfigurations;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #getFeedIds(java.lang.String)
   */
  @Override
  public Collection<String> getFeedIds(String namespace) throws FeedConfigStoreException {
    // Check that the namespace and feed configuration directories exist
    File feedConfigDir = checkNamespaceAndFeedConfigDir(namespace);

    List<String> feedIds = new ArrayList<String>();
    // Traverse all the files and store the filenames (without the
    // extension) as
    // feedIds
    for (String feedConfigFileName : feedConfigDir.list()) {
      if (feedConfigFileName.endsWith(FILE_EXTENSION)) {
        feedIds.add(feedConfigFileName.substring(0, feedConfigFileName.lastIndexOf(".")));
      }
    }

    // Sort the feedIds
    Collections.sort(feedIds);

    return feedIds;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #deleteFeed(java.lang.String, java.lang.String)
   */
  @Override
  public void deleteFeed(String namespace, String feedId, String userId)
      throws FeedConfigStoreException {
    // Construct and get the feed file handle
    File feedFile = getFeedConfigFilePath(namespace, feedId, userId);

    // Check that the feed file exists
    if (!feedFile.exists()) {
      throw new FeedConfigStoreException(Reason.FEED_DOES_NOT_EXIST,
          "No feed configuration exists with the given feedId : " + feedId
              + "  for the given namespace : " + namespace);
    }

    // Delete the feed configuration
    if (!feedFile.delete()) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Unable to delete the feed configuration for feedId : " + feedId
              + "  for the given namespace : " + namespace);
    }

  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #hasFeed(java.lang.String, java.lang.String)
   */
  @Override
  public boolean hasFeed(String namespace, String feedId, String userId) {
    // Get the handle to the feed config file
    File feedFile = getFeedConfigFilePath(namespace, feedId, userId);
    return feedFile.exists();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #updateFeed(java.lang.String,
   * org.apache.abdera.protocol.server.provider.managed.FeedConfiguration)
   */
  @Override
  public void updateFeed(String namespace, String userId, FeedConfiguration config)
      throws FeedConfigStoreException {
    // Get the feed configuration properties to save to file
    Properties prop = extractFeedConfigProperties(config);

    // Get a file handle to feed file location
    File feedFile = getFeedConfigFilePath(namespace, config.getFeedId(), userId);

    // Check that the file does not exist
    if (!feedFile.exists()) {
      logger.log(Level.WARNING, "The feed configuration file does not exist");
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "The feed configuration file does not exist");
    }

    // Write the values to the file
    writeToFile(prop, feedFile, false);
  }

  /**
   * Adds a adapter configuration with the given namespace. <br/>
   * It expects an instance of {@link NamespacedAdapterConfiguration} as
   * adapter configuration
   *
   * @throws FeedConfigStoreException
   *             If
   *             <ul>
   *             <li>The adapter configuration is not an instance of
   *             {@link NamespacedAdapterConfiguration}</li>
   *             <li>An adapter configuration with the given namepsace already
   *             exists</li>
   *             <li>Any I/O exceptions are encountered while saving the
   *             configuration details to the file system</li>
   *             </ul>
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   *      #addAdapterConfiguration(java.lang.String,
   *      org.apache.abdera.protocol.server
   *      .provider.managed.CollectionAdapterConfiguration)
   */
  @Override
  public void addAdapterConfiguration(String namespace, CollectionAdapterConfiguration config)
      throws FeedConfigStoreException {
    // Check for an instance that supports namespace
    if (!(config instanceof NamespacedAdapterConfiguration)) {
      logger.log(Level.SEVERE, "Expected an instance of : "
          + NamespacedAdapterConfiguration.class.getClass() + " but was : " + config.getClass());
      throw new FeedConfigStoreException(Reason.INVALID_ADAPTER_CONFIGURATION,
          "Please provide an instance of : " + NamespacedAdapterConfiguration.class);
    }

    // Create the namespace and adapter config directories if they are not
    // present
    createNamespaceAndAdapterConfigDir(namespace);

    // Type cast to the instance with namespace adapter configuration
    NamespacedAdapterConfiguration namespaceAdapterConfig = (NamespacedAdapterConfiguration) config;

    // Get the adapter config file
    File adapaterFile = getAdapterConfigFile(namespace, namespaceAdapterConfig.getAdapterName());

    // Check if the adapter configuration already exists
    if (adapaterFile.exists()) {
      logger.log(Level.WARNING, "Adapter configuration already exists!!");
      throw new FeedConfigStoreException(Reason.INVALID_ADAPTER_CONFIGURATION,
          "Adapter configuration already exists!!");
    }

    // Get the adapter config properties
    Properties prop = extractAdapterConfigProperties(namespaceAdapterConfig);

    // Write the contents to the file system
    writeToFile(prop, adapaterFile, true);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #updateAdapterConfiguration(java.lang.String,
   * org.apache.abdera.protocol.server
   * .provider.managed.CollectionAdapterConfiguration)
   */
  @Override
  public void updateAdapterConfiguration(String namespace, CollectionAdapterConfiguration config)
      throws FeedConfigStoreException {
    // Get the feed configuration properties to save to file
    Properties prop = extractAdapterConfigProperties((NamespacedAdapterConfiguration) config);

    // Get a file handle to feed file location
    File adapterConfigFile = getFeedConfigFilePath(
        namespace, ((NamespacedAdapterConfiguration) config).getAdapterName());

    // Check that the file does not exist
    if (!adapterConfigFile.exists()) {
      logger.log(Level.WARNING, "The adapter configuration file does not exist");
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "The adapter configuration file does not exist");
    }

    // Write the values to the file
    writeToFile(prop, adapterConfigFile, false);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #hasAdapterConfiguration(java.lang.String, java.lang.String)
   */
  @Override
  public boolean hasAdapterConfiguration(String namespace, String adapterName) {
    // Get the handle to the adapter config file
    File adapterFile = getAdapterConfigFile(namespace, adapterName);
    return adapterFile.exists();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #allowAdapter(java.lang.String, java.lang.String)
   */
  @Override
  public void allowAdapter(String namespace, String adapterId) throws FeedConfigStoreException {
    throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
        "This functionality is not implemented");
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #deleteAdapterConfiguration(java.lang.String, java.lang.String)
   */
  @Override
  public void deleteAdapterConfiguration(String namespace, String adapterId)
      throws FeedConfigStoreException {
    // Get the handle to the adapter file
    File adapterFile = getAdapterConfigFile(namespace, adapterId);

    // Check that the adapter file exists
    if (!adapterFile.exists()) {
      throw new FeedConfigStoreException(Reason.ADAPTER_CONFIG_DOES_NOT_EXIST,
          "No adapter configuration exists with the given adaptername : " + adapterId
              + "  for the given namespace : " + namespace);
    }

    // Delete the adapter configuration
    if (!adapterFile.delete()) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Unable to delete the adapter configuration for adaptername : " + adapterId
              + "  for the given namespace : " + namespace);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.feedserver.configstore.FeedConfigStore
   * #disallowAdapter(java.lang.String, java.lang.String)
   */
  @Override
  public void disallowAdapter(String namespace, String adapterId) throws FeedConfigStoreException {
    throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
        "This functionality is not implemented");
  }

  protected File getFeedConfigFilePath(String namespace, String feedId) {
    return getFeedConfigFilePath(namespace, feedId, null);
  }

  /**
   * Returns the feed configuration for the given feed under the given namespace
   *
   * @param namespace
   * @param feedId
   * @param userId
   * @return Feed config file path
   * @see com.google.feedserver.configstore.FeedConfigStore#getFeedConfiguration(String,
   *      String, String)
   */
  protected File getFeedConfigFilePath(String namespace, String feedId, String userId) {
    // Construct the feed file path
    String pathToFeedFile =
        new StringBuilder(FEED_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace)).append(
            "/").append(feedId).append(FILE_EXTENSION).toString();
    if (userId != null) {
      pathToFeedFile = pathToFeedFile.replaceFirst(FeedServerConfiguration.FEED_CONFIGURATION,
          FeedServerConfiguration.FEED_CONFIGURATION + "/user");
    }
    return new File(pathToFeedFile);
  }

  /**
   * Resolves referenced value in a configuration file
   * @param namespace Namespace
   * @param valueReference Reference to embedded value in the form of @fileName
   * @return Content of referenced file
   */
  @Override
  public String resolveFeedConfigReferencedValue(String namespace, String valueReference) {
    if (valueReference.startsWith(FileSystemConfigStoreUtil.FILE_INDICATOR)) {
      String path = new StringBuilder(
          FEED_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace))
          .append("/")
          .append(valueReference.substring(FileSystemConfigStoreUtil.FILE_INDICATOR.length()))
          .toString();
      try {
        return fileUtil.readFileContents(path);
      } catch (IOException e) {
        logger.severe(e.getMessage());
        return null;
      }
    } else {
      // no reference: return the value itself
      return valueReference;
    }
  }

  /**
   * Returns the handle to the adapter config file
   *
   * @param namespace The namespace
   * @param adapterName The adapter name used as filename
   * @return The handle to the adapter config file
   */
  private File getAdapterConfigFile(String namespace, String adapterName) {
    // Construct the feed file path
    String pathToAdapterConfigFile =
        new StringBuilder(ADAPTER_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace))
            .append("/").append(adapterName).append(FILE_EXTENSION).toString();
    // Get a file handle
    File adapterFile = new File(pathToAdapterConfigFile);

    return adapterFile;
  }

  /**
   * Reads the feed config properties from the given feed configuration and
   * returns them as name-value property pairs
   *
   * @param config The feed configuration
   * @return Feed config properties as name-value property pairs
   */
  private Properties extractFeedConfigProperties(FeedConfiguration config) {
    Map<String, Object> feedProperties = ((NamespacedFeedConfiguration) config).getProprties();

    Properties prop = new Properties();

    for (String feedAttributeKey : feedProperties.keySet()) {
      prop.setProperty(feedAttributeKey, (String) feedProperties.get(feedAttributeKey));
    }

    return prop;
  }

  /**
   * Extracts properties of the adapter configuration and returns them as
   * property name-value pairs
   *
   * @param namespaceAdapterConfig The adapater configuration
   * @return Name-value pairs of properties
   */
  private Properties extractAdapterConfigProperties(
      NamespacedAdapterConfiguration namespaceAdapterConfig) {
    Properties prop = new Properties();

    Map<String, Object> adapterConfigProperties = namespaceAdapterConfig.getProperties();

    for (String adapterConfigProperty : adapterConfigProperties.keySet()) {
      prop.setProperty(adapterConfigProperty, (String) adapterConfigProperties
          .get(adapterConfigProperty));
    }

    return prop;
  }

  /**
   * Writes the property values as contents to the given file
   *
   * @param prop The name-value property values
   * @param file The file to which the contents are to be written
   * @param createNewFile True if file has to be created and false if it has to
   *        be updated
   * @throws FeedConfigStoreException If any exception is encountered while
   *         writing the contents to the file
   */
  private void writeToFile(Properties prop, File file, boolean createNewFile)
      throws FeedConfigStoreException {
    try {
      // Check if a new file needs to be created
      if (createNewFile) {
        if (!file.createNewFile()) {
          throw new FeedConfigStoreException(Reason.INTERNAL_ERROR, "Unable to create file : "
              + file.getAbsolutePath() + " to write the configuration details");
        }
      }

      // Create an output stream and write to the file
      FileOutputStream outputStream = new FileOutputStream(file);
      prop.store(outputStream, "");
    } catch (FileNotFoundException e) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Problems encountered while locating the file : " + file.getAbsolutePath()
              + " to write the configuration details", e);
    } catch (IOException e) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Problems encountered while writing the configuration details to the file : "
              + file.getAbsolutePath(), e);
    }
  }

  /**
   * Creates directories for namepsace and feed configuration if they do not
   * exist
   *
   * @param namespace The namespace to be used
   * @throws FeedConfigStoreException If problems are encountered while creating
   *         the configuration directories
   */
  private void createNamespaceAndFeedConfigDir(String namespace) throws FeedConfigStoreException {
    // Create the namespace directory
    createNamespaceDirectory(namespace);

    File feedConfigDir =
        new File(
            new StringBuilder(FEED_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace))
                .toString());
    if (!feedConfigDir.exists()) {
      if (!feedConfigDir.mkdir()) {
        throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
            "Unable to create the feed configuration directory at the path : "
                + feedConfigDir.getAbsolutePath());
      }
    }
  }

  /**
   * Creates the namepsace directory
   *
   * @param namespace The namespace directory
   * @throws FeedConfigStoreException The problems are encountered while
   *         creating the directory
   */
  private void createNamespaceDirectory(String namespace) throws FeedConfigStoreException {
    String pathTonamespaceDirectory =
        new StringBuilder(BASE_CONFIGURATION_PATH).append("/").append(namespace).toString();
    File namespaceDirectory = new File(pathTonamespaceDirectory);

    // Check if the namespace directory exists and create it if it does not
    // exist
    if (!namespaceDirectory.isDirectory()) {
      if (!namespaceDirectory.mkdir()) {
        throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
            "Unable to create namespace directory at the given path : "
                + namespaceDirectory.getAbsolutePath());
      }
    }
  }

  /**
   * Creates directories for namepsace and adapter configuration if they do not
   * exist
   *
   * @param namespace The namespace to be used
   * @throws FeedConfigStoreException If problems are encountered while creating
   *         the configuration directories
   */
  private void createNamespaceAndAdapterConfigDir(String namespace) throws FeedConfigStoreException {
    // Create the namespace directory
    createNamespaceDirectory(namespace);

    // Check if the adapter configuration directory exists and
    // create it if it does not exist
    File adapterConfigDir =
        new File(new StringBuilder(ADAPTER_CONFIGURATION_PATH.replace(namespacePlaceHolder,
            namespace)).toString());
    if (!adapterConfigDir.exists()) {
      if (!adapterConfigDir.mkdir()) {
        throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
            "Unable to create the adapter configuration directory at the path : "
                + adapterConfigDir.getAbsolutePath());
      }
    }
  }

  /**
   * Returns the feed configuration with given feedId under the given
   * namespace.
   * <p>
   * The adapter configuratipon is also fetched and set as part of feed
   * configuration
   * </p>
   *
   * @param feedId
   *            The feed-id
   * @param feedConfigFilePath
   *            The handle to the feed config file
   * @return The feed configuration
   * @throws FileNotFoundException
   *             If feed config file is not found
   * @throws IOException
   *             If problems encountered while reading the config values
   * @throws FeedConfigStoreException
   *             Any feed store specific problems
   */
  private FeedConfiguration getFeedConfiguration(String namespace, String feedId,
      File feedConfigFilePath) throws FileNotFoundException, IOException, FeedConfigStoreException {
    // Load the properties file
    Properties prop = new Properties();
    InputStream is = FileSystemConfigStoreUtil.getInputStream(feedConfigFilePath.getPath());
    prop.load(is);

    String subUri = prop.getProperty(FeedConfiguration.PROP_SUB_URI_NAME);
    String adapterName = prop.getProperty(FeedServerConfiguration.FEED_ADAPTER_NAME_KEY);

    Map<String, Object> propertiesMap = new HashMap<String, Object>();
    propertiesMap.put(FeedServerConfiguration.ID_KEY, feedId);
    propertiesMap.put(FeedServerConfiguration.FEED_ADAPTER_NAME_KEY, adapterName);
    propertiesMap.put(FeedConfiguration.PROP_AUTHOR_NAME, prop
        .getProperty(FeedConfiguration.PROP_AUTHOR_NAME));
    propertiesMap.put(FeedConfiguration.PROP_TITLE_NAME, prop
        .getProperty(FeedConfiguration.PROP_TITLE_NAME));
    propertiesMap.put(FeedServerConfiguration.CONFIG_VALUE_KEY, prop
        .get(FeedServerConfiguration.CONFIG_VALUE_KEY));
    propertiesMap.put(FeedServerConfiguration.FEED_TYPE_CONFIG_KEY, prop
        .get(FeedServerConfiguration.FEED_TYPE_CONFIG_KEY));
    propertiesMap.put(FeedConfiguration.PROP_SUB_URI_NAME, subUri);

    // Get the adapter configuration
    NamespacedAdapterConfiguration adapterConfig = getAdapterConfig(namespace, adapterName);

    NamespacedFeedConfiguration feedConfig =
        new NamespacedFeedConfiguration(propertiesMap, adapterConfig,
            getNamesapceServerConfiguration(namespace));

    return feedConfig;
  }

  /**
   * Returns the adapter configuration with given adapter name under the given
   * namespace.
   * <p>
   * If the config value is a file indicated by '@' at the start of the config
   * value, then, the contents of the file are read and set as the config
   * value. <br/>
   * It adds the reference to feed specific database query file name in the
   * config value. <br/>
   * </p>
   *
   * @param namespace
   *            The namespace of the adapter config
   * @param adapterName
   *            The adapter name
   * @return The adapter configuration
   * @throws FeedConfigStoreException
   *             Any feed store specific problems
   */
  private NamespacedAdapterConfiguration getAdapterConfig(String namespace, String adapterName)
      throws FeedConfigStoreException {
    try {
      // Set the adapter config properties
      Map<String, Object> adapterConfigProperties =
          getAdapterConfigProperties(namespace, adapterName);

      // Load the target adapter properties
      Map<String, Object> adapterProperties =
          getAdapter(namespace, (String) adapterConfigProperties
              .get(FeedServerConfiguration.ADAPTER_TYPE_KEY));

      // Set the adapter classname
      adapterConfigProperties.put(FeedServerConfiguration.ADAPTER_TYPE_KEY, adapterProperties
          .get(ADAPTER_CLASS_NAME));

      adapterProperties.remove(ADAPTER_CLASS_NAME);
      adapterProperties.remove(FeedServerConfiguration.ID_KEY);

      // Config values from adapter config will override the config values from
      // adapter
      if (containsKeyWithNonNullValue(adapterConfigProperties,
          FeedServerConfiguration.CONFIG_VALUE_KEY)) {
        adapterProperties.remove(FeedServerConfiguration.CONFIG_VALUE_KEY);
        adapterProperties.remove(FeedServerConfiguration.CONFIG_VALUE_TYPE_KEY);
      }
      adapterConfigProperties.putAll(adapterProperties);

      NamespacedAdapterConfiguration adapterConfiguration =
          new NamespacedAdapterConfiguration(adapterConfigProperties,
              getNamesapceServerConfiguration(namespace));
      return adapterConfiguration;
    } catch (FileNotFoundException e) {
      throw new FeedConfigStoreException(Reason.ADAPTER_CONFIG_DOES_NOT_EXIST,
          "No adapter configuration exists with the given name : " + adapterName);
    } catch (IOException e) {
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Problems encountered while retrieving the adapter configuration with the given name : "
              + adapterName);
    }
  }

  /**
   * Retrieves and returns the adapter config properties stored under
   * 'AdapterConfig' by the given adapterName
   *
   * @param namespace The namespace under which the adapter config is stored
   * @param adapterName The adapter config name
   * @return The adapter config properties
   * @throws IOException If problems are encountered while reading and loading
   *         the adapter config properties
   * @throws FeedConfigStoreException
   */
  private Map<String, Object> getAdapterConfigProperties(String namespace, String adapterName)
      throws IOException, FeedConfigStoreException {
    // Load the adapter config
    File adapterConfigFile = getAdapterConfigFile(namespace, adapterName);

    Properties prop = new Properties();
    InputStream is = FileSystemConfigStoreUtil.getInputStreamForFile(adapterConfigFile);
    prop.load(is);

    // Set the adapter config properties
    Map<String, Object> adapterConfigProperties = new HashMap<String, Object>();
    adapterConfigProperties.put(FeedServerConfiguration.ID_KEY, adapterName);
    adapterConfigProperties.put(FeedServerConfiguration.ADAPTER_TYPE_KEY, prop
        .get(FeedServerConfiguration.ADAPTER_TYPE_KEY));
    setConfigValues(adapterConfigProperties, prop);
    // Setup the mixins
    if (prop.containsKey(FeedServerConfiguration.MIXINS)) {
      adapterConfigProperties.put(FeedServerConfiguration.MIXINS, getMixinConfigs(namespace, prop
          .getProperty(FeedServerConfiguration.MIXINS)));
    }
    return adapterConfigProperties;
  }

  /**
   * Checks if the specified property is in the map with a non-null value
   *
   * @param adapterConfigProperties The containing map
   * @param key The key to be checked
   * @return True if the key is in the map with a non-null value and false
   *         otherwise
   */
  private boolean containsKeyWithNonNullValue(Map<String, Object> adapterConfigProperties,
      String key) {
    return adapterConfigProperties.containsKey(key) && null != adapterConfigProperties.get(key);
  }

  /**
   * Returns the adapter details by reading the adapter from the 'Adapter'
   * directory
   *
   * @param namespace The namespace under which this adapter will reside
   * @param adapterName The name of the adapter. This should be the name of the
   *        file under 'Adapter' directory containing the details
   * @return The map containing all adapter properties
   * @throws FeedConfigStoreException
   */
  private Map<String, Object> getAdapter(String namespace, String adapterName)
      throws FeedConfigStoreException {
    Map<String, Object> adapterProperties = new HashMap<String, Object>();
    String filePath =
        new StringBuffer(ADAPTER_PATH.replace(namespacePlaceHolder, namespace)).append("/").append(
            adapterName).append(FILE_EXTENSION).toString();

    try {
      Properties prop = new Properties();
      prop.load(FileSystemConfigStoreUtil.getInputStream(filePath));

      // Set the adapter class name
      adapterProperties.put(ADAPTER_CLASS_NAME, prop.get(ADAPTER_CLASS_NAME));
      // Set the config values
      setConfigValues(adapterProperties, prop);

      if (ConfigStoreUtil.hasImplicitMixins(prop) && !ConfigStoreUtil.isWrapper(prop)) {
        MixinConfiguration[] implicitMixinConfigurations =
            getImplicitMixinConfigurationsForAdapter(namespace, prop);
        adapterProperties.put(FeedServerConfiguration.IMPLICIT_MIXINS, implicitMixinConfigurations);
      }

      return adapterProperties;
    } catch (IOException e) {
      logger.log(Level.WARNING, "Problems encountered while retrieving the adapter with name : "
          + adapterName, e);
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "Problems encountered while retrieving the adapter with name : " + adapterName);
    }
  }

  /**
   * Take the adapter properties and generate all the implicit mixin
   * configurations that you need for this adapter.
   *
   * @param namespace The namespace the adapter belongs to.
   * @param properties adapter Properties
   * @throws FeedConfigStoreException
   * @throws IOException
   */
  private MixinConfiguration[] getImplicitMixinConfigurationsForAdapter(String namespace,
      Properties properties) throws FeedConfigStoreException, IOException {
    List<MixinConfiguration> implicitMixinConfigurations = new ArrayList<MixinConfiguration>();

    String implicitMixins = (String) properties.get(FeedServerConfiguration.IMPLICIT_MIXINS);
    String[] mixinFileNames = FileSystemConfigStoreUtil.getListOfFileNames(implicitMixins);

    for (String mixinFileName : mixinFileNames) {
      String mixinFilePath =
          new StringBuilder(ADAPTER_PATH).append("/").append(mixinFileName).append(FILE_EXTENSION)
              .toString().replace(namespacePlaceHolder, namespace);
      Properties mixinProperties = new Properties();
      mixinProperties.load(FileSystemConfigStoreUtil.getInputStream(mixinFilePath));

      MapMixinConfiguration mixinConfig = new MapMixinConfiguration();
      mixinConfig.setWrapperName(mixinProperties.getProperty(ADAPTER_CLASS_NAME));
      if (ConfigStoreUtil.containsKeyWithNonNullValue(mixinProperties,
          FeedServerConfiguration.CONFIG_VALUE_KEY)) {
        mixinConfig.setWrapperConfig(mixinProperties.get(FeedServerConfiguration.CONFIG_VALUE_KEY)
            .toString().trim());
      } else {
        mixinConfig.setWrapperConfig("");
      }
      implicitMixinConfigurations.add(mixinConfig);
    }

    MixinConfiguration[] implicitMixinConfigs =
        new MixinConfiguration[implicitMixinConfigurations.size()];
    return implicitMixinConfigurations.toArray(implicitMixinConfigs);
  }

  /**
   * Checks if the config properties exist and adds them to the given map. If
   * the config value is a file name (starting with
   * {@link FileSystemConfigStoreUtil.FILE_INDICATOR}), the contants of the file
   * will be read and set as {@link FeedServerConfiguration.CONFIG_VALUE_KEY}
   *
   * @param configProperties The map of config properties
   * @param prop The properties to check
   * @throws IOException Thrown if problems are encountered while reading the
   *         file contents
   */
  private void setConfigValues(Map<String, Object> configProperties, Properties prop)
      throws IOException {
    if (prop.containsKey(FeedServerConfiguration.CONFIG_VALUE_TYPE_KEY)) {
      String configType = prop.getProperty(FeedServerConfiguration.CONFIG_VALUE_TYPE_KEY);
      String configValue = prop.getProperty(FeedServerConfiguration.CONFIG_VALUE_KEY);
      // If it is the path to a file, then read its content and set it as
      // configValue
      if (FileSystemConfigStoreUtil.checkIfStringIsFilePath(configValue)) {
        String[] configValues = FileSystemConfigStoreUtil.getListOfFileNames(configValue);
        StringBuilder configValueAsString = new StringBuilder();
        for (String configFileName : configValues) {
          configValueAsString.append(FileSystemConfigStoreUtil
              .getFileContents(BASE_CONFIGURATION_PATH + "/" + configFileName));
        }
        configProperties.put(FeedServerConfiguration.CONFIG_VALUE_KEY, configValueAsString
            .toString().trim());
      } else {
        configProperties.put(FeedServerConfiguration.CONFIG_VALUE_KEY, prop.getProperty(
            FeedServerConfiguration.CONFIG_VALUE_KEY).trim());
      }
      configProperties.put(FeedServerConfiguration.CONFIG_VALUE_TYPE_KEY, configType);
    }
  }

  /**
   * Reads the mixin configuration from the specified files, creates
   * {@link XmlMixinConfiguration} from the config
   *
   * @param namespace The namespace for which this mixin config is defined
   * @param mixins The list of mixin config
   * @return An array of mixin configurations
   * @throws FeedConfigStoreException If problems are encountered while
   *         retrieiving the mixin configuration
   */
  private MixinConfiguration[] getMixinConfigs(String namespace, String mixins)
      throws FeedConfigStoreException {

    List<MixinConfiguration> mixinConfigs = new ArrayList<MixinConfiguration>();

    if (FileSystemConfigStoreUtil.checkIfStringIsFilePath(mixins)) {
      String[] mixinConfigNames = FileSystemConfigStoreUtil.getListOfFileNames(mixins);
      for (String mixinFileName : mixinConfigNames) {
        mixinFileName =
            new StringBuilder(ADAPTER_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace))
                .append("/" + mixinFileName).append(MIXIN_FILE_EXTENSION).toString();

        String mixinContent;
        try {
          mixinContent = FileSystemConfigStoreUtil.getFileContents(mixinFileName);
          XmlMixinConfiguration mixin = new XmlMixinConfiguration(mixinContent.trim());
          mixinConfigs.addAll(processMixinConfig(namespace, mixin));
        } catch (IOException e) {
          logger.log(Level.WARNING, "Problems encountered while loading the mixin configuration"
              + mixinFileName, e);
          throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
              "Problems encountered while loading the mixin configuration : " + mixinFileName);
        }
      }
    } else {
      XmlMixinConfiguration mixin = new XmlMixinConfiguration(mixins.trim());
      mixinConfigs.addAll(processMixinConfig(namespace, mixin));
    }

    MixinConfiguration[] mixinConfig = new MixinConfiguration[mixinConfigs.size()];
    return mixinConfigs.toArray(mixinConfig);
  }

  /**
   * The method processes the mixin configuration to check if the given mixin
   * has defined any wrapper config
   *
   * <pre>
   * If there is no wrapper config, it has to be a mixin with adapter config
   * stored separately and hence retrieves it to set the wrapper name to the
   * adapter config type with the config value as the wrapper config
   * </pre>
   * <pre>
   * If the wrapper config is set it implies that the wrapper adapter needs to
   * be retrieved and set as wrapper class
   * </pre>
   *
   *
   * @param namespace The namespace for the given mixin
   * @param mixin The mixin to be processed
   * @return mixinConfigs List of mixin configs
   * @throws FeedConfigStoreException If errors are encountered while retrieving
   *         the adapterConfig OR adapter details pointed by the mixin
   */
  private List<MixinConfiguration> processMixinConfig(String namespace, MixinConfiguration mixin)
      throws FeedConfigStoreException {
    List<MixinConfiguration> mixinConfigs = new ArrayList<MixinConfiguration>();
    MapMixinConfiguration mixinConfig = new MapMixinConfiguration();
    if (null == mixin.getWrapperConfig()) {
      // This has to be a mixin with config stored seperately.
      NamespacedAdapterConfiguration adapterConfiguration =
          (NamespacedAdapterConfiguration) getAdapterConfiguration(namespace, mixin
              .getWrapperName());
      mixinConfig.setWrapperConfig(adapterConfiguration.getConfigData());
      for (MixinConfiguration adapterMixinConfig : adapterConfiguration.getMixins()) {
        mixinConfigs.add(adapterMixinConfig);
      }
      mixinConfig.setWrapperName(adapterConfiguration.getAdapterType());
    } else {
      // Just load the adapter config properties and use the adapter class name
      // as mixin wrapper name
      Map<String, Object> adapterWrapper = getAdapter(namespace, mixin.getWrapperName());
      mixinConfig.setWrapperName((String) adapterWrapper.get(ADAPTER_CLASS_NAME));
      mixinConfig.setWrapperConfig(mixin.getWrapperConfig());
    }
    mixinConfigs.add(mixinConfig);
    return mixinConfigs;
  }

  /**
   * Return an instance of the namespace server configuration
   *
   * @param namespace The namespace
   * @return The namespace configuration
   */
  private PerNamespaceServerConfiguration getNamesapceServerConfiguration(String namespace) {
    return new PerNamespaceServerConfiguration(FeedServerConfiguration.getIntance(), namespace);
  }

  /**
   * Checks if the namespace and feed config directories exist and creates them
   * if they do not exist
   *
   * @param namespace The namepsace
   * @return The file handle to feed config directory
   * @throws FeedConfigStoreException Any feed store exception
   */
  private File checkNamespaceAndFeedConfigDir(String namespace) throws FeedConfigStoreException {

    // Check and ensure tht namespace directory exists
    @SuppressWarnings("unused")
  File namespaceDirectory = checkNamespaceDirectory(namespace);

    String feedConfigPath =
        new StringBuilder(FEED_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace))
            .toString();

    File feedConfigDir = new File(feedConfigPath);

    // Check that the feed configuration directory exists
    if (!feedConfigDir.isDirectory()) {
      logger.log(Level.SEVERE,
          "There exists no feed configuration directory for the given namespace : " + namespace);
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "There is no feed configuration directory for the given namespace : " + namespace);
    }

    return feedConfigDir;
  }

  /**
   * Checks if the namepsace directory exists
   *
   * @param namespace The namespace
   * @return Th File handle to the namepsace directory
   * @throws FeedConfigStoreException If problems are encountered while checking
   *         and and getting a handle to the namespace directory
   */
  private File checkNamespaceDirectory(String namespace) throws FeedConfigStoreException {
    String pathTonamespaceDirectory =
        new StringBuilder(BASE_CONFIGURATION_PATH).append("/").append(namespace).toString();
    File namespaceDirectory = new File(pathTonamespaceDirectory);

    // Check that namespace directory exists
    if (!namespaceDirectory.isDirectory()) {
      logger.log(Level.SEVERE, "There exists no directory for the given namespace : " + namespace);
      throw new FeedConfigStoreException(Reason.DOMAIN_NOT_FOUND, "Invalid namespace : "
          + namespace);
    }

    return namespaceDirectory;
  }

  /**
   * Checks if the namespace and adapter config directories exist and creates
   * them if they do not exist
   *
   * @param namespace The namepsace
   * @return The file handle to adapter config directory
   * @throws FeedConfigStoreException Any feed store exception
   */
  private File checkNamespaceAndAdapterConfigDir(String namespace) throws FeedConfigStoreException {
    // Check and ensure tht namespace directory exists
    @SuppressWarnings("unused")
  File namespaceDirectory = checkNamespaceDirectory(namespace);

    String adapterConfigPath =
        new StringBuilder(ADAPTER_CONFIGURATION_PATH.replace(namespacePlaceHolder, namespace))
            .toString();

    File adapterConfigDir = new File(adapterConfigPath);

    // Check that the feed configuration directory exists
    if (!adapterConfigDir.isDirectory()) {
      logger.log(Level.SEVERE,
          "There exists no adapter configuration directory for the given namespace : " + namespace);
      throw new FeedConfigStoreException(Reason.INTERNAL_ERROR,
          "There is no adapter configuration directory for the given namespace : " + namespace);
    }

    return adapterConfigDir;
  }
}
TOP

Related Classes of com.google.feedserver.samples.configstore.SampleFileSystemFeedConfigStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.