/**
   * Actually executes pt-online-schema change. Does not generate any Sql.
   *
   * @return always <code>null</code>
   */
  @Override
  public Sql[] generate(Database database) {
    List<String> cmndline = buildCommand(database);
    log.info("Executing: " + filterCommands(cmndline));

    ProcessBuilder pb = new ProcessBuilder(cmndline);
    pb.redirectErrorStream(true);
    Process p = null;
    final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    final OutputStream tee =
        new FilterOutputStream(outputStream) {
          @Override
          public void write(int b) throws IOException {
            if (b == '\n') {
              log.info(outputStream.toString(Charset.defaultCharset().toString()));
              outputStream.reset();
            } else {
              super.write(b);
            }
          }
        };
    try {
      p = pb.start();
      final InputStream in = p.getInputStream();
      final InputStream err = p.getErrorStream();

      IOThread reader = new IOThread(in, tee);
      IOThread reader2 = new IOThread(err, tee);
      reader.start();
      reader2.start();

      int exitCode = p.waitFor();
      reader.join(5000);
      reader2.join(5000);
      // log the remaining output
      log.info(outputStream.toString(Charset.defaultCharset().toString()));

      if (exitCode != 0) {
        throw new RuntimeException("Percona exited with " + exitCode);
      }
    } catch (IOException e) {
      throw new UnexpectedLiquibaseException(e);
    } catch (InterruptedException e) {
      throw new UnexpectedLiquibaseException(e);
    } finally {
      if (p != null) {
        StreamUtil.closeQuietly(p.getErrorStream());
        StreamUtil.closeQuietly(p.getInputStream());
        StreamUtil.closeQuietly(p.getOutputStream());
        p.destroy();
      }
      StreamUtil.closeQuietly(outputStream);
    }
    return null;
  }
  private static void checkIsAvailableAndGetVersion() {
    ProcessBuilder pb = new ProcessBuilder(COMMAND, "--version");
    pb.redirectErrorStream(true);
    Process p = null;
    try {
      p = pb.start();
      p.waitFor();

      perconaToolkitVersion = StreamUtil.getStreamContents(p.getInputStream());
      if (perconaToolkitVersion != null) {
        perconaToolkitVersion = perconaToolkitVersion.replaceAll("\n|\r", "");
      }
      available = true;
      log.info("Using percona toolkit: " + perconaToolkitVersion);
    } catch (IOException e) {
      available = false;
    } catch (InterruptedException e) {
      available = false;
    } finally {
      if (p != null) {
        StreamUtil.closeQuietly(p.getErrorStream());
        StreamUtil.closeQuietly(p.getInputStream());
        StreamUtil.closeQuietly(p.getOutputStream());
        p.destroy();
      }
    }
  }
 @Override
 public void run() {
   try {
     StreamUtil.copy(from, to);
   } catch (IOException e) {
     log.debug("While copying streams", e);
   }
 }
  private List<Class> findClassesImpl(Class requiredInterface) throws Exception {
    logger.debug(
        "ServiceLocator finding classes matching interface " + requiredInterface.getName());

    List<Class> classes = new ArrayList<Class>();

    classResolver.addClassLoader(resourceAccessor.toClassLoader());
    for (Class<?> clazz :
        classResolver.findImplementations(
            requiredInterface, packagesToScan.toArray(new String[packagesToScan.size()]))) {
      if (clazz.getAnnotation(LiquibaseService.class) != null
          && clazz.getAnnotation(LiquibaseService.class).skip()) {
        continue;
      }

      if (!Modifier.isAbstract(clazz.getModifiers())
          && !Modifier.isInterface(clazz.getModifiers())
          && Modifier.isPublic(clazz.getModifiers())) {
        try {
          clazz.getConstructor();
          logger.debug(clazz.getName() + " matches " + requiredInterface.getName());

          classes.add(clazz);
        } catch (NoSuchMethodException e) {
          logger.info(
              "Can not use "
                  + clazz
                  + " as a Liquibase service because it does not have a no-argument constructor");
        } catch (NoClassDefFoundError e) {
          String message =
              "Can not use "
                  + clazz
                  + " as a Liquibase service because "
                  + e.getMessage().replace("/", ".")
                  + " is not in the classpath";
          if (e.getMessage().startsWith("org/yaml/snakeyaml")) {
            logger.info(message);
          } else {
            logger.warning(message);
          }
        }
      }
    }

    return classes;
  }
 protected PackageScanClassResolver defaultClassLoader() {
   if (WebSpherePackageScanClassResolver.isWebSphereClassLoader(
       this.getClass().getClassLoader())) {
     logger.debug("Using WebSphere Specific Class Resolver");
     return new WebSpherePackageScanClassResolver("liquibase/parser/core/xml/dbchangelog-2.0.xsd");
   } else {
     return new DefaultPackageScanClassResolver();
   }
 }
  public <T> Class<? extends T>[] findClasses(Class<T> requiredInterface)
      throws ServiceNotFoundException {
    logger.debug("ServiceLocator.findClasses for " + requiredInterface.getName());

    try {
      Class.forName(requiredInterface.getName());

      if (!classesBySuperclass.containsKey(requiredInterface)) {
        classesBySuperclass.put(requiredInterface, findClassesImpl(requiredInterface));
      }
    } catch (Exception e) {
      throw new ServiceNotFoundException(e);
    }

    List<Class> classes = classesBySuperclass.get(requiredInterface);
    HashSet<Class> uniqueClasses = new HashSet<Class>(classes);
    return uniqueClasses.toArray(new Class[uniqueClasses.size()]);
  }
Exemple #7
0
  public void rollback(Database database) throws RollbackFailedException {
    try {
      Executor executor = ExecutorService.getInstance().getExecutor(database);
      executor.comment("Rolling Back ChangeSet: " + toString());

      database.setObjectQuotingStrategy(objectQuotingStrategy);

      // set auto-commit based on runInTransaction if database supports DDL in transactions
      if (database.supportsDDLInTransaction()) {
        database.setAutoCommit(!runInTransaction);
      }

      RanChangeSet ranChangeSet = database.getRanChangeSet(this);
      if (hasCustomRollbackChanges()) {

        final List<SqlStatement> statements = new LinkedList<SqlStatement>();
        for (Change change : rollback.getChanges()) {
          if (((change instanceof DbmsTargetedChange))
              && !DatabaseList.definitionMatches(
                  ((DbmsTargetedChange) change).getDbms(), database, true)) {
            continue;
          }
          SqlStatement[] changeStatements = change.generateStatements(database);
          if (changeStatements != null) {
            statements.addAll(Arrays.asList(changeStatements));
          }
        }
        if (!statements.isEmpty()) {
          database.executeRollbackStatements(
              statements.toArray(new SqlStatement[] {}), sqlVisitors);
        }

      } else {
        List<Change> changes = getChanges();
        for (int i = changes.size() - 1; i >= 0; i--) {
          Change change = changes.get(i);
          database.executeRollbackStatements(change, sqlVisitors);
        }
      }

      if (runInTransaction) {
        database.commit();
      }
      log.debug("ChangeSet " + toString() + " has been successfully rolled back.");
    } catch (Exception e) {
      try {
        database.rollback();
      } catch (DatabaseException e1) {
        // ok
      }
      throw new RollbackFailedException(e);
    } finally {
      // restore auto-commit to false if this ChangeSet was not run in a transaction,
      // but only if the database supports DDL in transactions
      if (!runInTransaction && database.supportsDDLInTransaction()) {
        try {
          database.setAutoCommit(false);
        } catch (DatabaseException e) {
          throw new RollbackFailedException("Could not resetInternalState autocommit", e);
        }
      }
    }
  }
Exemple #8
0
  /**
   * This method will actually execute each of the changes in the list against the specified
   * database.
   *
   * @return should change set be marked as ran
   */
  public ExecType execute(
      DatabaseChangeLog databaseChangeLog, ChangeExecListener listener, Database database)
      throws MigrationFailedException {
    if (validationFailed) {
      return ExecType.MARK_RAN;
    }

    long startTime = new Date().getTime();

    ExecType execType = null;

    boolean skipChange = false;

    Executor executor = ExecutorService.getInstance().getExecutor(database);
    try {
      // set object quoting strategy
      database.setObjectQuotingStrategy(objectQuotingStrategy);

      // set auto-commit based on runInTransaction if database supports DDL in transactions
      if (database.supportsDDLInTransaction()) {
        database.setAutoCommit(!runInTransaction);
      }

      executor.comment("Changeset " + toString(false));
      if (StringUtils.trimToNull(getComments()) != null) {
        String comments = getComments();
        String[] lines = comments.split("\\n");
        for (int i = 0; i < lines.length; i++) {
          if (i > 0) {
            lines[i] = database.getLineComment() + " " + lines[i];
          }
        }
        executor.comment(StringUtils.join(Arrays.asList(lines), "\n"));
      }

      try {
        if (preconditions != null) {
          preconditions.check(database, databaseChangeLog, this);
        }
      } catch (PreconditionFailedException e) {
        if (listener != null) {
          listener.preconditionFailed(e, preconditions.getOnFail());
        }
        StringBuffer message = new StringBuffer();
        message.append(StreamUtil.getLineSeparator());
        for (FailedPrecondition invalid : e.getFailedPreconditions()) {
          message.append("          ").append(invalid.toString());
          message.append(StreamUtil.getLineSeparator());
        }

        if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.HALT)) {
          throw new MigrationFailedException(this, message.toString(), e);
        } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.CONTINUE)) {
          skipChange = true;
          execType = ExecType.SKIPPED;

          LogFactory.getLogger()
              .info(
                  "Continuing past: "
                      + toString()
                      + " despite precondition failure due to onFail='CONTINUE': "
                      + message);
        } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.MARK_RAN)) {
          execType = ExecType.MARK_RAN;
          skipChange = true;

          log.info(
              "Marking ChangeSet: "
                  + toString()
                  + " ran despite precondition failure due to onFail='MARK_RAN': "
                  + message);
        } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.WARN)) {
          execType = null; // already warned
        } else {
          throw new UnexpectedLiquibaseException(
              "Unexpected precondition onFail attribute: " + preconditions.getOnFail(), e);
        }
      } catch (PreconditionErrorException e) {
        if (listener != null) {
          listener.preconditionErrored(e, preconditions.getOnError());
        }

        StringBuffer message = new StringBuffer();
        message.append(StreamUtil.getLineSeparator());
        for (ErrorPrecondition invalid : e.getErrorPreconditions()) {
          message.append("          ").append(invalid.toString());
          message.append(StreamUtil.getLineSeparator());
        }

        if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.HALT)) {
          throw new MigrationFailedException(this, message.toString(), e);
        } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.CONTINUE)) {
          skipChange = true;
          execType = ExecType.SKIPPED;

        } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.MARK_RAN)) {
          execType = ExecType.MARK_RAN;
          skipChange = true;

          log.info(
              "Marking ChangeSet: " + toString() + " ran despite precondition error: " + message);
        } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.WARN)) {
          execType = null; // already logged
        } else {
          throw new UnexpectedLiquibaseException(
              "Unexpected precondition onError attribute: " + preconditions.getOnError(), e);
        }

        database.rollback();
      } finally {
        database.rollback();
      }

      if (!skipChange) {
        for (Change change : changes) {
          try {
            change.finishInitialization();
          } catch (SetupException se) {
            throw new MigrationFailedException(this, se);
          }
        }

        log.debug("Reading ChangeSet: " + toString());
        for (Change change : getChanges()) {
          if ((!(change instanceof DbmsTargetedChange))
              || DatabaseList.definitionMatches(
                  ((DbmsTargetedChange) change).getDbms(), database, true)) {
            if (listener != null) {
              listener.willRun(change, this, changeLog, database);
            }
            if (change.generateStatementsVolatile(database)) {
              executor.comment(
                  "WARNING The following SQL is possibly incorrect, invalid, and/or may change on each run:");
            }

            database.executeStatements(change, databaseChangeLog, sqlVisitors);
            log.info(change.getConfirmationMessage());
            if (listener != null) {
              listener.ran(change, this, changeLog, database);
            }
          } else {
            log.debug(
                "Change "
                    + change.getSerializedObjectName()
                    + " not included for database "
                    + database.getShortName());
          }
        }

        if (runInTransaction) {
          database.commit();
        }
        log.info(
            "ChangeSet "
                + toString(false)
                + " ran successfully in "
                + (new Date().getTime() - startTime + "ms"));
        if (execType == null) {
          execType = ExecType.EXECUTED;
        }
      } else {
        log.debug("Skipping ChangeSet: " + toString());
      }

    } catch (Exception e) {
      try {
        database.rollback();
      } catch (Exception e1) {
        throw new MigrationFailedException(this, e);
      }
      if (getFailOnError() != null && !getFailOnError()) {
        log.info(
            "Change set "
                + toString(false)
                + " failed, but failOnError was false.  Error: "
                + e.getMessage());
        log.debug("Failure Stacktrace", e);
        execType = ExecType.FAILED;
      } else {
        // just log the message, dont log the stacktrace by appending exception. Its logged anyway
        // to stdout
        log.severe("Change Set " + toString(false) + " failed.  Error: " + e.getMessage());
        if (e instanceof MigrationFailedException) {
          throw ((MigrationFailedException) e);
        } else {
          throw new MigrationFailedException(this, e);
        }
      }
    } finally {
      // restore auto-commit to false if this ChangeSet was not run in a transaction,
      // but only if the database supports DDL in transactions
      if (!runInTransaction && database.supportsDDLInTransaction()) {
        try {
          database.setAutoCommit(false);
        } catch (DatabaseException e) {
          throw new MigrationFailedException(this, "Could not resetInternalState autocommit", e);
        }
      }
    }
    return execType;
  }