Package com.google.appengine.tools.development

Source Code of com.google.appengine.tools.development.Modules

package com.google.appengine.tools.development;

import com.google.appengine.api.modules.ModulesServicePb.ModulesServiceError;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.ApiProxy.ApplicationException;
import com.google.apphosting.utils.config.AppEngineWebXml;
import com.google.apphosting.utils.config.AppEngineWebXml.ManualScaling;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Manager for {@link DevAppServer} servers.
*
*/
public class Modules implements ModulesController, ModulesFilterHelper {

  private static final AtomicReference<Modules> instance = new AtomicReference<Modules>();
  private static final Logger LOGGER = Logger.getLogger(Modules.class.getName());

  private final List<Module> modules;
  private final Map<String, Module> moduleNameToModuleMap;

  private final Lock dynamicConfigurationLock = new ReentrantLock();
  private static final int DYNAMIC_CONFIGURATION_TIMEOUT_SECONDS = 2;

  public static Modules createModules(
      ApplicationConfigurationManager applicationConfigurationManager,
      String serverInfo, File externalResourceDir, String address, DevAppServer devAppServer) {
    ImmutableList.Builder<Module> builder = ImmutableList.builder();
    for (ApplicationConfigurationManager.ModuleConfigurationHandle moduleConfigurationHandle :
      applicationConfigurationManager.getModuleConfigurationHandles()) {
      AppEngineWebXml appEngineWebXml =
          moduleConfigurationHandle.getModule().getAppEngineWebXml();
      Module module = null;
      if (!appEngineWebXml.getBasicScaling().isEmpty()) {
        module = new BasicModule(moduleConfigurationHandle, serverInfo, address, devAppServer,
            appEngineWebXml);
      } else if (!appEngineWebXml.getManualScaling().isEmpty()) {
        module = new ManualModule(moduleConfigurationHandle, serverInfo, address, devAppServer,
            appEngineWebXml);
      } else {
        module = new AutomaticModule(moduleConfigurationHandle, serverInfo, externalResourceDir,
            address, devAppServer);
      }
      builder.add(module);

      externalResourceDir = null;
    }
    instance.set(new Modules(builder.build()));
    return instance.get();
  }

  public static Modules getInstance() {
    return instance.get();
  }

  public void shutdown() throws Exception {
    for (Module module : modules) {
      module.shutdown();
    }
  }

  public void configure(Map<String, Object>containerConfigProperties) throws Exception {
    for (Module module : modules) {
      module.configure(containerConfigProperties);
    }
  }

  public void setApiProxyDelegate(ApiProxy.Delegate<?> apiProxyDelegate) {
    for (Module module : modules) {
      module.setApiProxyDelegate(apiProxyDelegate);
    }
  }

  public void createConnections() throws Exception {
    for (Module module : modules) {
      module.createConnection();
    }
  }

  public void startup() throws Exception {
    for (Module module : modules) {
      module.startup();
    }
  }

  public Module getMainModule() {
    return modules.get(0);
  }

  private Modules(List<Module> modules) {
    if (modules.size() < 1) {
      throw new IllegalArgumentException("modules must not be empty.");
    }
    this.modules = modules;

    ImmutableMap.Builder<String, Module> mapBuilder = ImmutableMap.builder();
    for (Module module : this.modules) {
      mapBuilder.put(module.getModuleName(), module);
    }
    moduleNameToModuleMap = mapBuilder.build();
  }

  public LocalServerEnvironment getLocalServerEnvironment() {
    return modules.get(0).getLocalServerEnvironment();
  }

  public Module getModule(String moduleName) {
    return moduleNameToModuleMap.get(moduleName);
  }

  @Override
  public Iterable<String> getModuleNames() {
    return moduleNameToModuleMap.keySet();
  }

  @Override
  public Iterable<String> getVersions(String moduleName) throws ApplicationException {
    return ImmutableList.of(getDefaultVersion(moduleName));
  }

  @Override
  public String getDefaultVersion(String moduleName) throws ApplicationException {
    Module module = getRequiredModule(moduleName);
    return module.getMainContainer().getAppEngineWebXmlConfig().getMajorVersionId();
  }

  @Override
  public int getNumInstances(String moduleName, String version) throws ApplicationException {
    Module module = getRequiredModule(moduleName);
    checkVersion(version, module);
    ManualScaling manualScaling = getRequiredManualScaling(module);
    return Integer.parseInt(manualScaling.getInstances());
  }

  @Override
  public void setNumInstances(String moduleName, String version, int numInstances)
      throws ApplicationException {
    throw new UnsupportedOperationException();
  }

  @Override
  public String getHostname(String moduleName, String version, int instance)
      throws ApplicationException {
    Module module = getRequiredModule(moduleName);
    if (instance != LocalEnvironment.MAIN_INSTANCE) {
      checkVersion(version, module);
      checkNotDynamicModule(module);
    }
    String hostAndPort = module.getHostAndPort(instance);
    if (hostAndPort == null) {
      throw new ApplicationException(ModulesServiceError.ErrorCode.INVALID_INSTANCES_VALUE,
          "Instance " + instance + " not found");
    }
    return hostAndPort;
  }

  @Override
  public ModuleState getModuleState(String moduleName) throws ApplicationException {
    return checkModuleStopped(moduleName) ? ModuleState.STOPPED : ModuleState.RUNNING;
  }

  @Override
  public String getScalingType(final String moduleName) throws ApplicationException {
    Module module = getModule(moduleName);
    if (module == null) {
      return null;
    }
    return module.getClass().getSimpleName();
  }

  @Override
  public void startModule(final String moduleName, final String version)
      throws ApplicationException {
    doDynamicConfiguration("startServing", new Runnable(){
      @Override
      public void run() {
        doStartModule(moduleName, version);
      }
    });
  }

  private void doStartModule(String moduleName, String version) {
    Module module = getRequiredModule(moduleName);
    checkVersion(version, module);
    checkNotDynamicModule(module);
    try {
      module.startServing();
    } catch (Exception e) {
      LOGGER.log(Level.SEVERE, "startServing failed", e);
      throw new ApplicationException(ModulesServiceError.ErrorCode.UNEXPECTED_STATE_VALUE,
          "startServing failed with error " + e.getMessage());
    }
  }

  @Override
  public void stopModule(final String moduleName, final String version)
      throws ApplicationException {
    doDynamicConfiguration("stopServing", new Runnable(){
      @Override
      public void run() {
        doStopModule(moduleName, version);
      }
    });
  }

  /**
   * Attempts to acquire the {@link #dynamicConfigurationLock} and run the
   * requested operation.
   * <p>
   * Currently only one dynamic configuration operation is allowed at a time. This
   * reduces complexity (e.g. we don't allow the user to start a module while we are
   * stopping it). One disadvantage of the approach is that some operations that may
   * work in production will not work in the development environment. In particular an
   * attempt to perform a dynamic configuration change in another thread during
   * a dynamic configuration change will time out. For example consider
   * {@link com.google.appengine.api.LifecycleManager#beginShutdown(long)}.
   *
   * @throws ApplicationException if the operation fails, we are unable to
   * acquire the lock in {@link #DYNAMIC_CONFIGURATION_TIMEOUT_SECONDS} seconds
   * or we are interrupted before we acquire the lock.
   */
  private void doDynamicConfiguration(String operation, Runnable runnable) {
    try {
      if (dynamicConfigurationLock.tryLock(DYNAMIC_CONFIGURATION_TIMEOUT_SECONDS,
          TimeUnit.SECONDS)) {
        try {
          runnable.run();
        } finally {
          dynamicConfigurationLock.unlock();
        }
      } else {
        LOGGER.log(Level.SEVERE, "stopServing timed out");
        throw new ApplicationException(ModulesServiceError.ErrorCode.UNEXPECTED_STATE_VALUE,
            operation + " timed out");
      }
    } catch (InterruptedException ie) {
      LOGGER.log(Level.SEVERE, "stopServing interrupted", ie);
      throw new ApplicationException(ModulesServiceError.ErrorCode.UNEXPECTED_STATE_VALUE,
          operation + " interrupted " + ie.getMessage());
    }
  }

  private void doStopModule(String moduleName, String version) {
    Module module = getRequiredModule(moduleName);
    checkVersion(version, module);
    checkNotDynamicModule(module);
    try {
      module.stopServing();
    } catch (Exception e) {
      LOGGER.log(Level.SEVERE, "stopServing failed", e);
      throw new ApplicationException(ModulesServiceError.ErrorCode.UNEXPECTED_STATE_VALUE,
          "stopServing failed with error " + e.getMessage());
    }
  }

  private Module getRequiredModule(String moduleName) {
    Module module = moduleNameToModuleMap.get(moduleName);
    if (module == null) {
      throw new ApplicationException(ModulesServiceError.ErrorCode.INVALID_MODULE_VALUE,
          "Module not found");
    }
    return module;
  }

  private void checkNotDynamicModule(Module module) {
    if (module.getMainContainer().getAppEngineWebXmlConfig().getManualScaling().isEmpty() &&
        module.getMainContainer().getAppEngineWebXmlConfig().getBasicScaling().isEmpty()) {
      LOGGER.warning("Module " + module.getModuleName() + " cannot be a dynamic module");
      throw new ApplicationException(ModulesServiceError.ErrorCode.INVALID_VERSION_VALUE,
          "This operation is not supported on Dynamic modules.");
    }
  }

  private ManualScaling getRequiredManualScaling(Module module) {
    ManualScaling manualScaling =
        module.getMainContainer().getAppEngineWebXmlConfig().getManualScaling();
    if (manualScaling.isEmpty()) {
      LOGGER.warning("Module " + module.getModuleName() + " must be a manual scaling module");
      throw new ApplicationException(ModulesServiceError.ErrorCode.INVALID_VERSION_VALUE,
          "Manual scaling is required.");
    }
    return manualScaling;
  }

  private void checkVersion(String version, Module module) {
    String moduleVersion =
        module.getMainContainer().getAppEngineWebXmlConfig().getMajorVersionId();
    if (version == null || !version.equals(moduleVersion)) {
      throw new ApplicationException(ModulesServiceError.ErrorCode.INVALID_VERSION_VALUE,
          "Version not found");
    }
  }

  @Override
  public boolean acquireServingPermit(
    String moduleName, int instanceNumber, boolean allowQueueOnBackends) {
    Module module = getModule(moduleName);
    InstanceHolder instanceHolder = module.getInstanceHolder(instanceNumber);
    return instanceHolder.acquireServingPermit();
  }

  @Override
  public int getAndReserveFreeInstance(String moduleName) {
    Module module = getModule(moduleName);
    InstanceHolder instanceHolder = module.getAndReserveAvailableInstanceHolder();
    return instanceHolder == null ? -1 : instanceHolder.getInstance();
  }

  @Override
  public void returnServingPermit(String moduleName, int instance) {
  }

  @Override
  public boolean checkInstanceExists(String moduleName, int instance) {
    Module module = getModule(moduleName);
    return module != null && module.getInstanceHolder(instance) != null;
  }

  @Override
  public boolean checkModuleExists(String moduleName) {
     return getModule(moduleName) != null;
  }

  @Override
  public boolean checkModuleStopped(String serverName) {
    return checkInstanceStopped(serverName, LocalEnvironment.MAIN_INSTANCE);
  }

  @Override
  public boolean checkInstanceStopped(String moduleName, int instance) {
    Module module = getModule(moduleName);
    InstanceHolder instanceHolder = module.getInstanceHolder(instance);
    return instanceHolder.isStopped();
  }

  @Override
  public void forwardToInstance(String requestedModule, int instance, HttpServletRequest hrequest,
      HttpServletResponse hresponse) throws IOException, ServletException {
    Module module = getModule(requestedModule);
    InstanceHolder instanceHolder = module.getInstanceHolder(instance);
    instanceHolder.getContainerService().forwardToServer(hrequest, hresponse);
  }

  @Override
  public boolean isLoadBalancingInstance(String moduleName, int instance) {
    Module module = getModule(moduleName);
    InstanceHolder instanceHolder = module.getInstanceHolder(instance);
    return instanceHolder.isLoadBalancingInstance();
  }

  @Override
  public boolean expectsGeneratedStartRequests(String moduleName,
      int instance) {
    Module module = getModule(moduleName);
    InstanceHolder instanceHolder = module.getInstanceHolder(instance);
    return instanceHolder.expectsGeneratedStartRequest();
  }

  @Override
  public int getPort(String moduleName, int instance) {
    Module module = getModule(moduleName);
    InstanceHolder instanceHolder = module.getInstanceHolder(instance);
    return instanceHolder.getContainerService().getPort();
  }
}
TOP

Related Classes of com.google.appengine.tools.development.Modules

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.