@Override
  @Transactional
  public Application start(Application application) throws ServiceException {
    try {
      User user = authentificationUtils.getAuthentificatedUser();
      logger.debug("start : Methods parameters : " + application);

      List<Module> modules = application.getModules();
      for (Module module : modules) {
        try {
          module = moduleService.startModule(module);
        } catch (ServiceException e) {
          logger.error("failed to start " + application.toString(), e);
        }
      }
      List<Server> servers = application.getServers();
      for (Server server : servers) {
        logger.info("old server ip : " + server.getContainerIP());
        server = serverService.startServer(server);
      }

      if (application.getAliases() != null && !application.getAliases().isEmpty()) {
        updateAliases(application);
      }
      logger.info("ApplicationService : Application successfully started ");
    } catch (PersistenceException e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
    return application;
  }
  @Override
  @Transactional
  public Application stop(Application application) throws ServiceException {

    try {
      List<Server> servers = application.getServers();
      for (Server server : servers) {
        server = serverService.stopServer(server);
      }
      List<Module> modules = application.getModules();
      for (Module module : modules) {
        try {
          module = moduleService.stopModule(module);
        } catch (ServiceException e) {
          logger.error(
              "ApplicationService Error : failed to stop " + application.getName() + " : " + e);
        }
      }
      logger.info("ApplicationService : Application successfully stopped ");
    } catch (PersistenceException e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }

    return application;
  }
  @Override
  @Transactional
  public void addNewAlias(Application application, String alias)
      throws ServiceException, CheckException {

    logger.info("ALIAS VALUE IN addNewAlias : " + alias);

    if (checkAliasIfExists(alias)) {
      throw new CheckException(
          "This alias is already used by another application on this CloudUnit instance");
    }

    alias = alias.toLowerCase();
    if (alias.startsWith("https://") || alias.startsWith("http://") || alias.startsWith("ftp://")) {
      alias = alias.substring(alias.lastIndexOf("//") + 2, alias.length());
    }

    if (!StringUtils.isAlphanumeric(alias)) {
      throw new CheckException(
          "This alias must be alphanumeric. Please remove all other characters");
    }

    try {
      Server server = application.getServers().get(0);
      application.getAliases().add(alias);
      hipacheRedisUtils.writeNewAlias(alias, application, server.getServerAction().getServerPort());
      applicationDAO.save(application);

    } catch (DataAccessException e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
  }
  /**
   * Lancer par signal de NoPublicController quand le processus sshd est démarré dans les containers
   * serveur et git
   */
  public Application updateEnv(Application application, User user) throws ServiceException {

    logger.info("--update Env of Server--");
    String command = null;
    Map<String, String> configShellModule = new HashMap<>();
    Map<String, String> configShellServer = new HashMap<>();

    Module moduleGit = moduleService.findGitModule(user.getLogin(), application);
    Server server = application.getServers().get(0);

    String rootPassword = application.getUser().getPassword();
    configShellModule.put("port", moduleGit.getSshPort());
    configShellModule.put("dockerManagerAddress", moduleGit.getApplication().getManagerIp());
    configShellModule.put("password", rootPassword);
    configShellModule.put("dockerManagerAddress", application.getManagerIp());
    logger.info("new server ip : " + server.getContainerIP());
    try {
      int counter = 0;
      while (!server.getStatus().equals(Status.START)
          || !moduleGit.getStatus().equals(Status.START)) {
        if (counter == 100) {
          break;
        }
        Thread.sleep(1000);
        logger.info(" wait git and server sshd processus start");
        logger.info(
            "SSHDSTATUS = server : " + server.getStatus() + " - module : " + moduleGit.getStatus());
        moduleGit = moduleService.findById(moduleGit.getId());
        server = serverService.findById(server.getId());
        counter++;
      }
      command = ". /cloudunit/scripts/update-env.sh " + server.getContainerIP();
      logger.info("command shell to execute [" + command + "]");

      shellUtils.executeShell(command, configShellModule);

      configShellServer.put("port", server.getSshPort());
      configShellServer.put("dockerManagerAddress", server.getApplication().getManagerIp());
      configShellServer.put("password", rootPassword);
      command = ". /cloudunit/scripts/rm-auth-keys.sh ";
      logger.info("command shell to execute [" + command + "]");

      shellUtils.executeShell(command, configShellServer);
      String cleanCommand = server.getServerAction().cleanCommand();
      if (cleanCommand != null) {
        shellUtils.executeShell(server.getServerAction().cleanCommand(), configShellServer);
      }
    } catch (Exception e) {
      moduleGit.setStatus(Status.FAIL);
      moduleGit = moduleService.saveInDB(moduleGit);
      server.setStatus(Status.FAIL);
      server = serverService.saveInDB(server);
      logger.error("Error :  Error during update Env var of GIT " + e);
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
    return application;
  }
 /**
  * Methode permettant de mettre l'application dans un état particulier pour se prémunir d'éventuel
  * problème de concurrence au niveau métier
  */
 @Override
 @Transactional
 public void setStatus(Application application, Status status) throws ServiceException {
   try {
     Application _application = applicationDAO.findOne(application.getId());
     _application.setStatus(status);
     application.setStatus(status);
     applicationDAO.saveAndFlush(_application);
   } catch (PersistenceException e) {
     throw new ServiceException(e.getLocalizedMessage(), e);
   }
 }
  @Override
  @Transactional
  public Application deploy(File file, Application application)
      throws ServiceException, CheckException {

    int code = -1;
    Map<String, String> configShell = new HashMap<>();

    try {
      // get app with all its components

      for (Server server : application.getServers()) {

        // loading server ssh informations

        String rootPassword = server.getApplication().getUser().getPassword();
        configShell.put("port", server.getSshPort());
        configShell.put("dockerManagerAddress", application.getManagerIp());
        configShell.put("password", rootPassword);
        String destFile = "/cloudunit/tmp/";

        // send the file on container

        shellUtils.sendFile(
            file, rootPassword, server.getSshPort(), application.getManagerIp(), destFile);

        // call deployment script

        code =
            shellUtils.executeShell(
                "bash /cloudunit/scripts/deploy.sh "
                    + file.getName()
                    + " "
                    + application.getUser().getLogin(),
                configShell);
      }

      // if all is ok, create a new deployment tag and set app to starting

      if (code == 0) {
        deploymentService.create(application, Type.WAR);
      } else {
        throw new CheckException("No way to deploy application " + file + ", " + application);
      }

    } catch (Exception e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }

    return application;
  }
  @Override
  @Transactional
  public void updateAliases(Application application) throws ServiceException {
    try {
      Server server = application.getServers().get(0);
      List<String> aliases = applicationDAO.findAllAliases(application.getName());
      for (String alias : aliases) {
        hipacheRedisUtils.updateAlias(alias, application, server.getServerAction().getServerPort());
      }

    } catch (DataAccessException e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
  }
 @Override
 @Transactional
 public Application saveGitPush(Application application, String login)
     throws ServiceException, CheckException {
   logger.info("parameters - application : " + application.toString());
   deploymentService.create(application, Type.GITPUSH);
   return application;
 }
 @Override
 public List<String> getListAliases(Application application) throws ServiceException {
   try {
     return applicationDAO.findAllAliases(application.getName());
   } catch (DataAccessException e) {
     throw new ServiceException(e.getLocalizedMessage(), e);
   }
 }
  /**
   * Methode qui teste la validité d'une application
   *
   * @param applicationName
   * @param serverName
   * @throws ServiceException
   * @throws CheckException
   */
  public void isValid(String applicationName, String serverName)
      throws ServiceException, CheckException {
    logger.info("--CALL APP IS VALID--");
    Application application = new Application();
    logger.info("applicationName = " + applicationName + ", serverName = " + serverName);

    User user = authentificationUtils.getAuthentificatedUser();
    if (user == null) {
      throw new CheckException("User is not authentificated");
    }

    application.setName(applicationName);
    application.setUser(user);
    application.setModules(new ArrayList<>());

    this.checkCreate(application, serverName);
  }
 /**
  * Method useful for Logs and Monitoring Management
  *
  * @return
  * @throws ServiceException
  */
 @Override
 public List<Application> findAll() throws ServiceException {
   try {
     logger.debug("start findAll");
     List<Application> listApplications = applicationDAO.findAll();
     for (Application application : listApplications) {
       application.setServers(serverService.findByApp(application));
       application.setModules(
           moduleService.findByAppAndUser(application.getUser(), application.getName()));
     }
     logger.debug("ApplicationService : All Applications found ");
     return listApplications;
   } catch (PersistenceException e) {
     logger.error("Error ApplicationService : error findAll Method : " + e);
     throw new ServiceException(e.getLocalizedMessage(), e);
   }
 }
  /**
   * Add git container to application associated to server
   *
   * @param application
   * @return
   * @throws ServiceException
   * @throws CheckException
   */
  private Module addGitContainer(Application application, String tagName)
      throws ServiceException, CheckException {

    Module moduleGit = ModuleFactory.getModule("git");
    // todo : externaliser la variable
    String containerGitAddress = "/cloudunit/git/.git";

    try {
      // Assign fixed host ports for forwarding git ports (22)
      Map<String, String> mapProxyPorts = portUtils.assignProxyPorts(application);
      String freeProxySshPortNumber = mapProxyPorts.get("freeProxySshPortNumber");

      // Creation of git container fo application
      moduleGit.setName("git");
      moduleGit.setImage(imageService.findByName("git"));
      moduleGit.setApplication(application);

      moduleGit.setSshPort(freeProxySshPortNumber);
      moduleGit = moduleService.initModule(application, moduleGit, tagName);

      application.getModules().add(moduleGit);
      application.setGitContainerIP(moduleGit.getContainerIP());

      application.setGitSshProxyPort(freeProxySshPortNumber);

      // Update GIT respository informations in the current application
      application.setGitAddress(
          "ssh://"
              + AlphaNumericsCharactersCheckUtils.convertToAlphaNumerics(
                  application.getUser().getLogin())
              + "@"
              + application.getName()
              + "."
              + application.getSuffixCloudUnitIO().substring(1)
              + ":"
              + application.getGitSshProxyPort()
              + containerGitAddress);

      moduleGit.setStatus(Status.START);
      moduleGit = moduleService.update(moduleGit);

    } catch (UnsupportedEncodingException e) {
      moduleGit.setStatus(Status.FAIL);
      logger.error("Error :  Error during persist git module " + e);
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
    return moduleGit;
  }
 @Override
 @Transactional
 public void removeAlias(Application application, String alias)
     throws ServiceException, CheckException {
   try {
     hipacheRedisUtils.removeAlias(alias);
     boolean removed = application.getAliases().remove(alias);
     if (!removed) {
       throw new CheckException("Alias [" + alias + "] doesn't exist");
     }
     application = applicationDAO.save(application);
   } catch (DataAccessException e) {
     throw new ServiceException(e.getLocalizedMessage(), e);
   }
 }
  /**
   * Test if the user can create new applications because we limit the number per user
   *
   * @param application
   * @param serverName
   * @throws CheckException
   * @throws ServiceException
   */
  @Override
  public void checkCreate(Application application, String serverName)
      throws CheckException, ServiceException {

    logger.debug("--CHECK APP COUNT--");

    if (this.countApp(application.getUser()) >= Integer.parseInt(numberMaxApplications)) {
      throw new ServiceException(
          "You have already created your " + numberMaxApplications + " apps into the Cloud");
    }

    try {
      if (checkAppExist(application.getUser(), application.getName())) {
        throw new CheckException(messageSource.getMessage("app.exists", null, locale));
      }
      if (imageService.findByName(serverName) == null)
        throw new CheckException(messageSource.getMessage("image.not.found", null, locale));
      imageService.findByName(serverName);

    } catch (PersistenceException e) {
      logger.error("ApplicationService Error : Create Application" + e);
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
  }
  @Transactional
  public void deployToContainerId(
      String applicationName, String containerId, File file, String destFile)
      throws ServiceException {

    try {
      Application application =
          this.findByNameAndUser(authentificationUtils.getAuthentificatedUser(), applicationName);

      Map<String, String> configShell = new HashMap<>();

      String sshPort = application.getSShPortByContainerId(containerId);
      String rootPassword = application.getUser().getPassword();
      configShell.put("port", sshPort);
      configShell.put("dockerManagerAddress", application.getManagerIp());
      configShell.put("password", rootPassword);

      // send the file on container
      shellUtils.sendFile(file, rootPassword, sshPort, application.getManagerIp(), destFile);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  public List<ContainerUnit> listContainers(String applicationName, boolean withModules)
      throws ServiceException {
    List<ContainerUnit> containers = new ArrayList<>();
    try {
      Application application =
          findByNameAndUser(authentificationUtils.getAuthentificatedUser(), applicationName);
      if (application != null) {
        try {
          // Serveurs
          List<Server> servers = application.getServers();
          // Ajout des containers de type server
          for (Server server : servers) {
            DockerContainer dockerContainer = new DockerContainer();
            dockerContainer.setName(server.getName());
            dockerContainer = DockerContainer.findOne(dockerContainer, application.getManagerIp());
            server = containerMapper.mapDockerContainerToServer(dockerContainer, server);
            ContainerUnit containerUnit =
                new ContainerUnit(server.getName(), server.getContainerID(), "server");
            containers.add(containerUnit);
          }
          if (withModules) {
            // Ajout des containers de type module
            List<Module> modules = application.getModules();
            for (Module module : modules) {
              // on evite de remonter les modules de type toolkit
              // (git, maven...)
              if (module.isTool()) {
                continue;
              }
              DockerContainer dockerContainer = new DockerContainer();
              dockerContainer.setName(module.getName());
              dockerContainer =
                  DockerContainer.findOne(dockerContainer, application.getManagerIp());
              module = containerMapper.mapDockerContainerToModule(dockerContainer, module);
              ContainerUnit containerUnit =
                  new ContainerUnit(module.getName(), module.getContainerID(), "module");
              containers.add(containerUnit);
            }
          }
        } catch (Exception ex) {
          // Si une application sort en erreur, il ne faut pas
          // arrêter la suite des traitements
          logger.error(application.toString(), ex);
        }
      }

    } catch (Exception e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
    return containers;
  }
  /**
   * Lancer par signal de NoPublicController quand le processus sshd est (re)démarré dans container
   * serveur et git, pour mettre à jour la nouvelle IP du serveur
   */
  @Override
  public Application sshCopyIDToServer(Application application, User user) throws ServiceException {
    String command = null;
    Map<String, String> configShell = new HashMap<>();

    Module moduleGit = moduleService.findGitModule(user.getLogin(), application);

    if (logger.isDebugEnabled()) {
      logger.debug("--ssh Copy ID To Server--");
      logger.debug("ssh port : " + moduleGit.getSshPort());
      logger.debug("manager ip : " + application.getManagerIp());
    }

    for (Server server : application.getServers()) {
      configShell.put("password", server.getApplication().getUser().getPassword());
      configShell.put("port", moduleGit.getSshPort());
      configShell.put("dockerManagerAddress", application.getManagerIp());
      configShell.put("userLogin", server.getApplication().getUser().getLogin());

      try {
        int counter = 0;
        while (!server.getStatus().equals(Status.START)
            || !moduleGit.getStatus().equals(Status.START)) {
          if (counter == 100) {
            break;
          }
          Thread.sleep(1000);
          logger.info(" wait git and server ssh processus start");
          logger.info(
              "STATUS = server : " + server.getStatus() + " - module : " + moduleGit.getStatus());

          moduleGit = moduleService.findById(moduleGit.getId());
          server = serverService.findById(server.getId());
          counter++;
        }

        // To permit ssh access on server from git container
        command =
            "expect /cloudunit/scripts/ssh-copy-id-expect.sh "
                + moduleGit.getApplication().getUser().getPassword();
        logger.info("command shell to execute [" + command + "]");

        shellUtils.executeShell(command, configShell);

      } catch (Exception e) {
        moduleGit.setStatus(Status.FAIL);
        moduleGit = moduleService.saveInDB(moduleGit);
        server.setStatus(Status.FAIL);
        server = serverService.saveInDB(server);
        logger.error("Error :  Error during permit git to access to server " + e);

        throw new ServiceException(e.getLocalizedMessage(), e);
      }
    }

    try {
      moduleGit = moduleService.update(moduleGit);

      application.getModules().add(moduleGit);
      application.setGitContainerIP(moduleGit.getContainerIP());

    } catch (ServiceException e) {
      moduleGit.setStatus(Status.FAIL);
      moduleService.saveInDB(moduleGit);
      logger.error("Error :  Error during persist git module " + e);
      throw new ServiceException(e.getLocalizedMessage(), e);
    }
    logger.info(
        "ApplicationService : Application " + application.getName() + " successfully created.");
    return application;
  }
  @Override
  @Transactional(rollbackFor = ServiceException.class)
  public Application create(String applicationName, String login, String serverName, String tagName)
      throws ServiceException, CheckException {

    // if tagname is null, we prefix with a ":"
    if (tagName != null) {
      tagName = ":" + tagName;
    }
    if (applicationName != null) {
      applicationName = applicationName.toLowerCase();
    }

    logger.info("--CALL CREATE NEW APP--");
    Application application = new Application();

    logger.info("applicationName = " + applicationName + ", serverName = " + serverName);

    User user = authentificationUtils.getAuthentificatedUser();

    // For cloning management
    if (tagName != null) {
      application.setAClone(true);
    }

    application.setName(applicationName);
    application.setUser(user);
    application.setModules(new ArrayList<>());

    // verify if application exists already
    this.checkCreate(application, serverName);

    // todo : use a session flag
    application.setStatus(Status.PENDING);

    application = this.saveInDB(application);
    serverService.checkMaxNumberReach(application);

    String subdomain = System.getenv("CU_SUB_DOMAIN") == null ? "" : System.getenv("CU_SUB_DOMAIN");

    List<Image> imagesEnabled = imageService.findEnabledImages();
    List<String> imageNames = new ArrayList<>();
    for (Image image : imagesEnabled) {
      imageNames.add(image.getName());
    }

    if (!imageNames.contains(serverName)) {
      throw new CheckException(messageSource.getMessage("server.not.found", null, locale));
    }

    try {
      // BLOC APPLICATION
      application.setDomainName(subdomain + suffixCloudUnitIO);
      application = applicationDAO.save(application);
      application.setManagerIp(dockerManagerIp);
      application.setJvmRelease(javaVersionDefault);
      application.setRestHost(restHost);
      logger.info(application.getManagerIp());

      // BLOC SERVER
      Server server = ServerFactory.getServer(serverName);
      // We get image associated to server
      Image image = imageService.findByName(serverName);
      server.setImage(image);
      server.setApplication(application);
      server.setName(serverName);
      server = serverService.create(server, tagName);

      List<Server> servers = new ArrayList<>();
      servers.add(server);
      application.setServers(servers);

      // BLOC MODULE
      Module moduleGit = this.addGitContainer(application, tagName);
      application.getModules().add(moduleGit);
      application.setGitContainerIP(moduleGit.getContainerIP());

      // Persistence for Application model
      application = applicationDAO.save(application);

      // Copy the ssh key from the server to git container to be able to deploy war with gitpush
      // During clone processus, env variables are not updated. We must wait for a restart before
      // to copy the ssh keys for git push
      if (tagName == null) {
        this.sshCopyIDToServer(application, user);
      }

    } catch (DataAccessException e) {
      throw new ServiceException(e.getLocalizedMessage(), e);
    }

    logger.info("" + application);
    logger.info(
        "ApplicationService : Application " + application.getName() + " successfully created.");

    return application;
  }
  /**
   * Remove an application
   *
   * @param application
   * @param user
   * @return
   * @throws ServiceException
   */
  @Override
  @Transactional
  public Application remove(Application application, User user)
      throws ServiceException, CheckException {

    try {
      logger.info("Starting removing application " + application.getName());

      // Delete all modules
      List<Module> listModules = application.getModules();
      for (Module module : listModules) {
        try {
          moduleService.remove(application, user, module, false, application.getStatus());
        } catch (ServiceException | CheckException e) {
          application.setStatus(Status.FAIL);
          logger.error(
              "ApplicationService Error : failed to remove module "
                  + module.getName()
                  + " for application "
                  + application.getName()
                  + " : "
                  + e);
          e.printStackTrace();
        }
      }

      // Delete all alias
      List<String> aliases = new ArrayList<>();
      aliases.addAll(application.getAliases());
      for (String alias : aliases) {
        removeAlias(application, alias);
      }

      // Delete all servers
      List<Server> listServers = application.getServers();
      for (Server server : listServers) {
        serverService.remove(server.getName());
        if (listServers.indexOf(server) == listServers.size() - 1) {
          hipacheRedisUtils.removeRedisAppKey(application);
          applicationDAO.delete(server.getApplication());
          portUtils.releaseProxyPorts(application);
        }
      }

      logger.info("ApplicationService : Application successfully removed ");

    } catch (PersistenceException e) {
      setStatus(application, Status.FAIL);
      logger.error(
          "ApplicationService Error : failed to remove " + application.getName() + " : " + e);

      throw new ServiceException(e.getLocalizedMessage(), e);
    } catch (ServiceException e) {
      setStatus(application, Status.FAIL);
      logger.error(
          "ApplicationService Error : failed to remove application "
              + application.getName()
              + " : "
              + e);
    }
    return application;
  }