/** * 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()]); }
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); } } } }
/** * 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; }