Package com.mockey.storage.xml

Source Code of com.mockey.storage.xml.MockeyXmlFileManager

/*
* This file is part of Mockey, a tool for testing application
* interactions over HTTP, with a focus on testing web services,
* specifically web applications that consume XML, JSON, and HTML.
* Copyright (C) 2009-2010  Authors:
*
* chad.lafontaine (chad.lafontaine AT gmail DOT com)
* neil.cronin (neil AT rackle DOT com)
* lorin.kobashigawa (lkb AT kgawa DOT com)
* rob.meyer (rob AT bigdis DOT com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*
*/
package com.mockey.storage.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.google.common.io.CharStreams;
import com.mockey.model.ProxyServerModel;
import com.mockey.model.Scenario;
import com.mockey.model.Service;
import com.mockey.model.ServicePlan;
import com.mockey.model.ServiceRef;
import com.mockey.model.TwistInfo;
import com.mockey.model.Url;
import com.mockey.storage.IMockeyStorage;
import com.mockey.storage.StorageRegistry;
import com.mockey.ui.ServiceMergeResults;

/**
* Consumes an XML file and configures Mockey services.
*
* <pre>
* + mock_service_definitions.xml
* + mockey_def_depot
* ++ <SERVICE NAME>
* ++ <SERVICE NAME>
* +++ <NAME>.xml
* +++ scenarios
* ++++ 1.xml
* ++++ 2.xml
*
* </pre>
*
* @author Chad.Lafontaine
*
*/
public class MockeyXmlFileManager {

  private static Logger logger = Logger.getLogger(MockeyXmlFileManager.class);
  private static final char[] VALID_FILE_NAME_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
      .toCharArray();

  private File basePathFile = new File(System.getProperty("user.dir"));
  private static IMockeyStorage store = StorageRegistry.MockeyStorage;
  private static MockeyXmlFileManager mockeyXmlFileManagerInstance = null;
  public static final String MOCK_SERVICE_DEFINITION = "mock_service_definitions.xml";

  protected static final String MOCK_SERVICE_FOLDER = "mockey_def_depot";
  protected static final String MOCK_SERVICE_SCENARIO_FOLDER = "scenarios";

  public static final String FILESEPERATOR = System.getProperty("file.separator");

  /**
   * Basic constructor. Will create a folder on the file system to store XML
   * definitions.
   *
   */
  private MockeyXmlFileManager(String path) {

    if (path != null && path.trim().length() > 0) {
      this.basePathFile = new File(path);
      if (!this.basePathFile.exists()) {
        this.basePathFile.mkdir();
      }
    } else {
      this.basePathFile = new File(System.getProperty("user.dir"));
    }

    File fileDepot = new File(this.getBasePathFile(), MOCK_SERVICE_FOLDER);
    if (!fileDepot.exists()) {
      boolean success = fileDepot.mkdirs();
      if (!success) {
        logger.fatal("Unable to create a folder called " + MOCK_SERVICE_FOLDER);
      } else {
        logger.info("Created directory: " + fileDepot.getAbsolutePath());
      }
    }
  }

  public static MockeyXmlFileManager getInstance() {
    if (MockeyXmlFileManager.mockeyXmlFileManagerInstance == null) {
      MockeyXmlFileManager.createInstance(System.getProperty("user.dir"));
    }
    return MockeyXmlFileManager.mockeyXmlFileManagerInstance;
  }

  /**
   *
   * @param path
   */
  public static void createInstance(String path) {

    MockeyXmlFileManager.mockeyXmlFileManagerInstance = new MockeyXmlFileManager(path);

  }

  /**
   *
   * @return location of Mockey definitions.
   */
  public File getBasePathFile() {
    return this.basePathFile;
  }

  /**
   *
   * @param file
   *            - xml configuration file for Mockey
   * @throws IOException
   * @throws SAXException
   * @throws SAXParseException
   */
  public String getFileContentAsString(InputStream fstream) throws IOException, SAXParseException, SAXException {

    String inputStreamString = CharStreams.toString(new InputStreamReader(fstream, "UTF-8"));
    return inputStreamString;

  }

  /**
   * Loads from default file definition file.
   *
   * @return results of loading configuration, includes additions and possible
   *         conflicts.
   *
   * @throws SAXParseException
   * @throws IOException
   */
  public ServiceMergeResults loadConfiguration() throws SAXParseException, IOException {
    File n = new File(this.getBasePathFile(), MOCK_SERVICE_DEFINITION);
    logger.debug("Loading configuration from " + MOCK_SERVICE_DEFINITION);

    try {

      return loadConfigurationWithXmlDef(getFileContentAsString(new FileInputStream(n)), null);
    } catch (SAXException e) {
      logger.error("Ouch, unable to parse" + n.getAbsolutePath(), e);
    }
    return new ServiceMergeResults();
  }

  /**
   *
   * @param strXMLDefintion
   * @param tagArguments
   * @return results (conflicts and additions).
   * @throws IOException
   * @throws SAXParseException
   * @throws SAXException
   */
  public ServiceMergeResults loadConfigurationWithXmlDef(String strXMLDefintion, String tagArguments)
      throws IOException, SAXParseException, SAXException {
    ServiceMergeResults mergeResults = new ServiceMergeResults();

    // ***** REMEMBER *****
    // Every time a saveOrUpdateXXXX is made, the entire STORE is written to
    // the file system.
    // If the STORE has many definitions, then each SAVE will loop over
    // every file and write.
    //
    // NOT GOOD FOR PERFORMANCE
    //
    // Solution: put the store in a temporary transient state
    // (memory-mode-only), then revert to original transient setting,
    // which could have been in memory-only or write-to-file in the
    // first place.
    //
    // *********************
    Boolean originalTransientState = store.getReadOnlyMode();
    store.setReadOnlyMode(true);

    // STEP #1. CREATE A TEMP STORE
    // Read the incoming XML file, and create a new/temporary store for the
    // need to ensure current store doesn't get overridden
    //
    MockeyXmlFileConfigurationReader msfr = new MockeyXmlFileConfigurationReader();
    IMockeyStorage mockServiceStoreTemporary = msfr.readDefinition(strXMLDefintion);

    // STEP #2. PROXY SETTINGS
    // If the proxy settings are _empty_, then set the incoming
    // proxy settings. Otherwise, call out a merge conflict.
    //
    ProxyServerModel proxyServerModel = store.getProxy();
    if (proxyServerModel.hasSettings()) {
      mergeResults.addConflictMsg("Proxy settings NOT set from incoming file.");
    } else {
      store.setProxy(mockServiceStoreTemporary.getProxy());
      mergeResults.addAdditionMsg("Proxy settings set.");
    }

    // STEP #3. BUILD SERVICE REFERENCES
    // Why is this needed?
    // We are adding _new_ services into the Store, and that means that the
    // store's state is always changing. We need references as a saved
    // snapshot list of store state prior to adding new services.
    // **********
    // I forget why we really need this though...
    // **********
    List<Service> serviceListFromRefs = new ArrayList<Service>();
    for (ServiceRef serviceRef : mockServiceStoreTemporary.getServiceRefs()) {
      try {

        String mockServiceDefinition = getFileContentAsString(new FileInputStream(serviceRef.getFileName()));

        // HACK:
        // I tried to find an easier way to use XML ENTITY and let
        // Digester
        // to the work of slurping up the XML but was unsuccessful.
        // Hence, the brute force.
        // YYYYY
        //

        List<Service> tmpList = msfr.readServiceDefinition(mockServiceDefinition);
        for (Service tmpService : tmpList) {
          serviceListFromRefs.add(tmpService);
        }
      } catch (SAXParseException spe) {
        logger.error("Unable to parse file of name " + serviceRef.getFileName(), spe);
        mergeResults.addConflictMsg("File not parseable: " + serviceRef.getFileName());

      } catch (FileNotFoundException fnf) {
        logger.error("File not found: " + serviceRef.getFileName());
        mergeResults.addConflictMsg("File not found: " + serviceRef.getFileName());
      }

    }
    addServicesToStore(mergeResults, serviceListFromRefs, tagArguments);

    // STEP #4. MERGE SERVICES AND SCENARIOS
    // Since this gets complicated, logic was moved to it's own method.
    mergeResults = addServicesToStore(mergeResults, mockServiceStoreTemporary.getServices(), tagArguments);

    // STEP #5. UNIVERSAL RESPONSE SETTINGS
    // Important: usage of the temporary-store's Scenario reference
    // information is used to set the primary in-memory store. The primary
    // store has all the information and the TEMP store only needs to pass
    // the references, e.g. Service 1, Scenario 2.
    if (store.getUniversalErrorScenario() != null
        && mockServiceStoreTemporary.getUniversalErrorScenarioRef() != null) {
      mergeResults.addConflictMsg("Universal error message already defined with name '"
          + store.getUniversalErrorScenario().getScenarioName() + "'");
    } else if (store.getUniversalErrorScenario() == null
        && mockServiceStoreTemporary.getUniversalErrorScenarioRef() != null) {
      store.setUniversalErrorScenarioRef(mockServiceStoreTemporary.getUniversalErrorScenarioRef());
      mergeResults.addAdditionMsg("Universal error response defined.");

    }

    // STEP #6. MERGE SERVICE PLANS
    for (ServicePlan servicePlan : mockServiceStoreTemporary.getServicePlans()) {
      if (tagArguments != null) {
        servicePlan.addTagToList(tagArguments);
      }
      store.saveOrUpdateServicePlan(servicePlan);
    }

    // STEP #7. TWIST CONFIGURATION
    for (TwistInfo twistInfo : mockServiceStoreTemporary.getTwistInfoList()) {
      store.saveOrUpdateTwistInfo(twistInfo);
    }

    // STEP #8. DEFAULT Service Plan ID
    // Only set a default service plan ID from the incoming XML file
    // if one is not already set in the current store.
    ServicePlan servicePlan = mockServiceStoreTemporary.getServicePlanById(mockServiceStoreTemporary
        .getDefaultServicePlanIdAsLong());
    if (servicePlan != null && store.getDefaultServicePlanIdAsLong() == null) {
      // OK, we have a 'default' service plan from the incoming file AND
      // the current store does not. Let's update the current store.
      store.setDefaultServicePlanId(mockServiceStoreTemporary.getDefaultServicePlanId());
      store.setServicePlan(servicePlan);

    }

    // Don't forget to set state back to original state.
    // NOTE: if transient state (read only) is false, then this method will
    // write to STORE to the file system.
    // Yeah!
    // *********************
    store.setReadOnlyMode(originalTransientState);
    // *********************

    return mergeResults;
  }

  // Let's Merge!
  private ServiceMergeResults addServicesToStore(ServiceMergeResults mergeResults, List<Service> serviceListToAdd,
      String tagArguments) {
    // When loading a definition file, by default, we should
    // compare the uploaded Service list mock URL to what's currently
    // in memory.
    //
    // 1) MATCHING MOCK URL
    // If there is an existing/matching mockURL, then this isn't
    // a new service and we DON'T want to overwrite. But, we
    // want new Scenarios if they exist. See Scenario.equals()
    //
    // 2) NO MATCHING MOCK URL
    // If there is no matching service URL, then we want to create a new
    // service and associated scenarios. But here's an odd case. What if
    // we are merging two same-name Services, each with empty matching URL
    // lists?
    //

    for (Service uploadedServiceBean : serviceListToAdd) {
      List<Service> serviceBeansInMemory = store.getServices();
      Iterator<Service> inMemoryServiceIter = serviceBeansInMemory.iterator();

      boolean existingService = false;
      Service inMemoryServiceBean = null;

      while (inMemoryServiceIter.hasNext()) {
        inMemoryServiceBean = (Service) inMemoryServiceIter.next();

        // Same name?
        if (uploadedServiceBean.getServiceName().trim().toLowerCase()
            .equals(inMemoryServiceBean.getServiceName().trim().toLowerCase())) {
          existingService = true;
          mergeResults.addConflictMsg("Service '" + uploadedServiceBean.getServiceName()
              + "' not created because one with the same name already defined. '"
              + inMemoryServiceBean.getServiceName() + "' ");

        }
      }
      if (!existingService) {
        // YES, no in-store matching Name.
        // We null ID, to not write-over on any in-store
        // services with same ID
        uploadedServiceBean.setId(null);

        // #TAG HANDLING - BEGIN
        // Ensure Service, and all it's child scenarios have
        // incoming/uploaded tag arguments
        uploadedServiceBean.addTagToList(tagArguments);
        for (Scenario scenarioTmp : uploadedServiceBean.getScenarios()) {
          scenarioTmp.addTagToList(tagArguments);
        }
        // #TAG HANDLING - END

        // Save to the IN-MEMORY STORE
        store.saveOrUpdateService(uploadedServiceBean);
        mergeResults.addAdditionMsg("Uploaded Service '" + uploadedServiceBean.getServiceName()
            + "' created with scenarios.");

      } else {
        // We have an existing Service
        // Just merge scenarios per matching services
        mergeResults = this.mergeServices(uploadedServiceBean, inMemoryServiceBean, mergeResults, tagArguments);
      }

    }
    return mergeResults;
  }

  /**
   * This method will make an effort to take things that exist in the
   *
   * @param uploadedService
   * @param inMemoryService
   * @param readResults
   * @return
   */
  public ServiceMergeResults mergeServices(Service uploadedService, Service inMemoryService

  , ServiceMergeResults readResults, String tagArguments) {

    Boolean originalMode = store.getReadOnlyMode();
    store.setReadOnlyMode(true);

    if (uploadedService != null && inMemoryService != null
        && uploadedService.getServiceName().trim().equalsIgnoreCase(inMemoryService.getServiceName().trim())

    ) {

      // ********************** TAG - BEGIN ***********************
      // #TAG HANDLING for the Service - BEGIN
      // Ensure Service gets incoming/uploaded-file tag arguments
      inMemoryService.addTagToList(tagArguments);
      // Ensure Service gets uploaded Service tag arguments
      inMemoryService.addTagToList(uploadedService.getTag());
      // #TAG HANDLING for the Service - END
      // ********************** TAG - END *****************

      // ********************* SCENARIOS BEGIN *******************
      if (readResults == null) {
        readResults = new ServiceMergeResults();
      }

      Iterator<Scenario> uploadedListIter = uploadedService.getScenarios().iterator();
      Iterator<Scenario> inMemListIter = inMemoryService.getScenarios().iterator();

      while (uploadedListIter.hasNext()) {
        Scenario uploadedScenario = (Scenario) uploadedListIter.next();
        boolean inMemScenarioExistTemp = false;
        Scenario inMemScenarioTemp = null;

        while (inMemListIter.hasNext()) {
          inMemScenarioTemp = (Scenario) inMemListIter.next();

          if (inMemScenarioTemp.hasSameNameAndResponse(uploadedScenario)) {

            inMemScenarioExistTemp = true;
            break;
          }
        }
        if (!inMemScenarioExistTemp) {

          // Hey, we have a new scenario.
          // NOTE: incoming/uploaded scenario has an ID.
          // We MUST nullify it, to ensure there's no common Service's
          // scenario's ID
          uploadedScenario.setId(null);
          uploadedScenario.setServiceId(inMemoryService.getId());
          // Tag for Service:Scenario
          uploadedScenario.addTagToList(tagArguments);
          inMemoryService.saveOrUpdateScenario(uploadedScenario);

          readResults.addAdditionMsg("Scenario name '" + uploadedScenario.getScenarioName()
              + "' from uploaded service named '" + uploadedService.getServiceName()
              + "' was merged into service '" + inMemoryService.getServiceName() + "' ");
        } else {
          // OK, we have a MATCHING Scenario.
          // Be sure to add the uploaded-file tags
          inMemScenarioTemp.addTagToList(tagArguments);
          // Be sure to add the uploaded-scenario tags
          inMemScenarioTemp.addTagToList(uploadedScenario.getTag());
          // Save the scenario to the Service
          inMemoryService.saveOrUpdateScenario(inMemScenarioTemp);

          // Although we still need to
          readResults.addConflictMsg("Uploaded Scenario '" + uploadedScenario.getScenarioName()
              + "' not added, already defined in in-memory service '" + inMemoryService.getServiceName()
              + "' ");
        }

      }
      // ********************* SCENARIOS - END ******************

      // ********************* REAL URLS - BEGIN *******************
      for (Url uploadedUrl : uploadedService.getRealServiceUrls()) {
        inMemoryService.saveOrUpdateRealServiceUrl(uploadedUrl);
      }
      // ********************* REAL URLS - END *******************
      store.saveOrUpdateService(inMemoryService);

    }

    store.setReadOnlyMode(originalMode);
    return readResults;
  }

  /**
   *
   * @param service
   * @return
   */
  protected File getServiceFile(Service service) {
    // Ensure the name is good.

    String serviceFileName = service.getServiceName();

    if (serviceFileName != null) {
      serviceFileName = getSafeForFileSystemName(serviceFileName);

      File serviceDirectoryFile = new File(this.getBasePathFile(), MockeyXmlFileManager.MOCK_SERVICE_FOLDER
          + FILESEPERATOR + serviceFileName);
      // depot directory/<service ID> directory
      if (!serviceDirectoryFile.exists()) {
        serviceDirectoryFile.mkdir();
      }
      // depot directory/<service ID> directory/scenario directory/
      File serviceScenarioListDirectory = new File(serviceDirectoryFile.getPath() + FILESEPERATOR
          + MOCK_SERVICE_SCENARIO_FOLDER);
      if (!serviceScenarioListDirectory.exists()) {
        serviceScenarioListDirectory.mkdir();
      }
      // depot directory/<service ID> directory/<service ID> file
      File serviceFile = new File(serviceDirectoryFile.getPath() + FILESEPERATOR + serviceFileName + ".xml");
      return serviceFile;
    } else {
      return null;
    }

  }

  protected File[] getServiceScenarioFileNames(Service service) {

    File serviceScenarioDir = new File(this.getBasePathFile(), getSafeForFileSystemName(service.getServiceName())
        + FILESEPERATOR + MOCK_SERVICE_SCENARIO_FOLDER);
    return serviceScenarioDir.listFiles();

  }

  private File getServiceScenarioDirectoryAbsolutePath(Service service, Scenario scenario) {
    // mockey_def_depot/<service ID>/scenarios/<scenario_name>.xml
    File serviceScenarioFolder = new File(this.getBasePathFile(), MockeyXmlFileManager.MOCK_SERVICE_FOLDER
        + FILESEPERATOR + getSafeForFileSystemName(service.getServiceName()) + FILESEPERATOR
        + MOCK_SERVICE_SCENARIO_FOLDER);

    return serviceScenarioFolder;
  }

  protected File getServiceScenarioFileAbsolutePath(Service service, Scenario scenario) {
    // mockey_def_depot/<service ID>/scenarios/<scenario_name>.xml
    File serviceScenarioFolder = getServiceScenarioDirectoryAbsolutePath(service, scenario);
    File serviceScenarioFile = new File(serviceScenarioFolder.getPath() + FILESEPERATOR
        + getScenarioXmlFileName(scenario));

    return serviceScenarioFile;
  }

  /**
   * Example if file is here:
   * <pre>
   * // /Users/<User>/Work/Mockey/dist/mockey_def_depot/<ServiceName>/scenarios/<ScenarioName>.xml
   * </pre>
   * then returns
   * <pre>
   * mockey_def_depot/<ServiceName>/scenarios/<ScenarioName>.xml
   * </pre>
   * @param service
   * @param scenario
   * @return a path name relative to the root mockey depot folder.
   * 
   */
  protected String getServiceScenarioFileRelativePathToDepotFolder(Service service, Scenario scenario) {
   
    String relativePath = MockeyXmlFileManager.MOCK_SERVICE_FOLDER + FILESEPERATOR
        + getSafeForFileSystemName(service.getServiceName()) + FILESEPERATOR + MOCK_SERVICE_SCENARIO_FOLDER
        + FILESEPERATOR + getSafeForFileSystemName(scenario.getScenarioName()) + ".xml";

    return relativePath;
  }


  /**
   *
   * @param scenario
   * @return
   */
  public String getScenarioResponseFileName(Scenario scenario) {
    return getSafeForFileSystemName(scenario.getScenarioName()) + ".txt";
  }

  /**
   *
   * @param scenario
   * @return
   */
  public String getScenarioXmlFileName(Scenario scenario) {
    return getSafeForFileSystemName(scenario.getScenarioName()) + ".xml";
  }

  /**
   *
   * @param arg
   * @return a file name safe for a file system.
   * @see MockeyXmlFileManager#VALID_FILE_NAME_CHARS
   *
   */
  public static String getSafeForFileSystemName(String arg) {

    // Let's make sure we only accept valid characters (AlphaNumberic +
    // '_').
    StringBuffer safe = new StringBuffer();
    for (int x = 0; x < arg.length(); x++) {
      boolean valid = false;
      for (int i = 0; i < VALID_FILE_NAME_CHARS.length; i++) {
        if (arg.charAt(x) == VALID_FILE_NAME_CHARS[i]) {
          valid = true;
          break;
        }
      }
      if (valid) {
        safe.append(arg.charAt(x));
      }
    }
    return safe.toString().toLowerCase();

    //
  }

  /**
   * @param baseFile
   * @param fileArg
   * @return the RELATIVE path of the fileArg if a child of base file.
   *         Otherwise, returns the absolute path of the fileArg.
   */
  public String getRelativePath(File fileArg) {
    String relativePath = "";
    String basePath = this.getBasePathFile().getAbsolutePath();
    String filePath = fileArg.getAbsolutePath();
    int index = filePath.indexOf(basePath);
    if (index > -1) {
      try {
        //
        relativePath = fileArg.getAbsolutePath().substring(index + basePath.length() + 1);
      } catch (Exception e) {
        logger.error("Unable to retrive a relative path from: " + fileArg.getAbsolutePath()
            + "' with base path '" + basePath + "'", e);
        relativePath = "ERROR";
      }
    } else {
      relativePath = "ERROR";
      logger.error("Unable to retrive a relative path from: " + fileArg.getAbsolutePath() + "' with base path '"
          + basePath + "'");
    }
    return relativePath;
  }

}
TOP

Related Classes of com.mockey.storage.xml.MockeyXmlFileManager

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.