liquibase.diff.output.changelog.DiffToChangeLog Maven / Gradle / Ivy
package liquibase.diff.output.changelog;
import liquibase.change.Change;
import liquibase.changelog.ChangeSet;
import liquibase.configuration.GlobalConfiguration;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.database.ObjectQuotingStrategy;
import liquibase.database.OfflineConnection;
import liquibase.database.core.DB2Database;
import liquibase.database.core.OracleDatabase;
import liquibase.diff.DiffResult;
import liquibase.diff.ObjectDifferences;
import liquibase.diff.compare.CompareControl;
import liquibase.diff.output.DiffOutputControl;
import liquibase.exception.DatabaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.logging.LogFactory;
import liquibase.serializer.ChangeLogSerializer;
import liquibase.serializer.ChangeLogSerializerFactory;
import liquibase.serializer.core.xml.XMLChangeLogSerializer;
import liquibase.statement.core.RawSqlStatement;
import liquibase.structure.DatabaseObject;
import liquibase.structure.DatabaseObjectComparator;
import liquibase.structure.core.Column;
import liquibase.util.DependencyUtil;
import liquibase.util.StringUtils;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class DiffToChangeLog {
private String idRoot = String.valueOf(new Date().getTime());
private int changeNumber = 1;
private String changeSetContext;
private String changeSetAuthor;
private String changeSetPath;
private DiffResult diffResult;
private DiffOutputControl diffOutputControl;
private static Set loggedOrderFor = new HashSet();
public DiffToChangeLog(DiffResult diffResult, DiffOutputControl diffOutputControl) {
this.diffResult = diffResult;
this.diffOutputControl = diffOutputControl;
}
public DiffToChangeLog(DiffOutputControl diffOutputControl) {
this.diffOutputControl = diffOutputControl;
}
public void setDiffResult(DiffResult diffResult) {
this.diffResult = diffResult;
}
public void setChangeSetContext(String changeSetContext) {
this.changeSetContext = changeSetContext;
}
public void print(String changeLogFile) throws ParserConfigurationException, IOException, DatabaseException {
ChangeLogSerializer changeLogSerializer = ChangeLogSerializerFactory.getInstance().getSerializer(changeLogFile);
this.print(changeLogFile, changeLogSerializer);
}
public void print(PrintStream out) throws ParserConfigurationException, IOException, DatabaseException {
this.print(out, new XMLChangeLogSerializer());
}
public void print(String changeLogFile, ChangeLogSerializer changeLogSerializer) throws ParserConfigurationException, IOException, DatabaseException {
File file = new File(changeLogFile);
if (!file.exists()) {
LogFactory.getLogger().info(file + " does not exist, creating");
FileOutputStream stream = new FileOutputStream(file);
print(new PrintStream(stream), changeLogSerializer);
stream.close();
} else {
LogFactory.getLogger().info(file + " exists, appending");
ByteArrayOutputStream out = new ByteArrayOutputStream();
print(new PrintStream(out), changeLogSerializer);
String xml = new String(out.toByteArray());
String innerXml = xml.replaceFirst("(?ms).*]*>", "");
innerXml = innerXml.replaceFirst(" ", "");
innerXml = innerXml.trim();
if ("".equals(innerXml)) {
LogFactory.getLogger().info("No changes found, nothing to do");
return;
}
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
String line;
long offset = 0;
boolean foundEndTag = false;
while ((line = randomAccessFile.readLine()) != null) {
int index = line.indexOf("");
if (index >= 0) {
foundEndTag = true;
break;
} else {
offset = randomAccessFile.getFilePointer();
}
}
String lineSeparator = LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputLineSeparator();
if (foundEndTag) {
randomAccessFile.seek(offset);
randomAccessFile.writeBytes(" ");
randomAccessFile.write(innerXml.getBytes());
randomAccessFile.writeBytes(lineSeparator);
randomAccessFile.writeBytes("" + lineSeparator);
} else {
randomAccessFile.seek(0);
randomAccessFile.write(xml.getBytes());
}
randomAccessFile.close();
// BufferedWriter fileWriter = new BufferedWriter(new
// FileWriter(file));
// fileWriter.append(xml);
// fileWriter.close();
}
}
/**
* Prints changeLog that would bring the target database to be the same as
* the reference database
*/
public void print(PrintStream out, ChangeLogSerializer changeLogSerializer) throws ParserConfigurationException, IOException, DatabaseException {
List changeSets = generateChangeSets();
changeLogSerializer.write(changeSets, out);
out.flush();
}
public List generateChangeSets() {
final ChangeGeneratorFactory changeGeneratorFactory = ChangeGeneratorFactory.getInstance();
DatabaseObjectComparator comparator = new DatabaseObjectComparator();
String created = null;
if (LiquibaseConfiguration.getInstance().getProperty(GlobalConfiguration.class, GlobalConfiguration.GENERATE_CHANGESET_CREATED_VALUES).getValue(Boolean.class)) {
created = new SimpleDateFormat("yyyy-MM-dd HH:mmZ").format(new Date());
}
List changeSets = new ArrayList();
List> types = getOrderedOutputTypes(MissingObjectChangeGenerator.class);
List missingObjects = new ArrayList();
for (Class extends DatabaseObject> type : types) {
for (DatabaseObject object : diffResult.getMissingObjects(type, new DatabaseObjectComparator() {
@Override
public int compare(DatabaseObject o1, DatabaseObject o2) {
if (o1 instanceof Column && o1.getAttribute("order", Integer.class) != null && o2.getAttribute("order", Integer.class) != null) {
int i = o1.getAttribute("order", Integer.class).compareTo(o2.getAttribute("order", Integer.class));
if (i != 0) {
return i;
}
}
return super.compare(o1, o2);
}
})) {
if (object == null) {
continue;
}
if (!diffResult.getReferenceSnapshot().getDatabase().isLiquibaseObject(object) && !diffResult.getReferenceSnapshot().getDatabase().isSystemObject(object)) {
missingObjects.add(object);
}
}
}
for (DatabaseObject object : sortMissingObjects(missingObjects, diffResult.getReferenceSnapshot().getDatabase())) {
ObjectQuotingStrategy quotingStrategy = ObjectQuotingStrategy.QUOTE_ALL_OBJECTS;
Change[] changes = changeGeneratorFactory.fixMissing(object, diffOutputControl, diffResult.getReferenceSnapshot().getDatabase(), diffResult.getComparisonSnapshot().getDatabase());
addToChangeSets(changes, changeSets, quotingStrategy, created);
}
types = getOrderedOutputTypes(UnexpectedObjectChangeGenerator.class);
for (Class extends DatabaseObject> type : types) {
ObjectQuotingStrategy quotingStrategy = diffOutputControl.getObjectQuotingStrategy();
for (DatabaseObject object : diffResult.getUnexpectedObjects(type, comparator)) {
if (!diffResult.getComparisonSnapshot().getDatabase().isLiquibaseObject(object) && !diffResult.getComparisonSnapshot().getDatabase().isSystemObject(object)) {
Change[] changes = changeGeneratorFactory.fixUnexpected(object, diffOutputControl, diffResult.getReferenceSnapshot().getDatabase(), diffResult.getComparisonSnapshot().getDatabase());
addToChangeSets(changes, changeSets, quotingStrategy, created);
}
}
}
types = getOrderedOutputTypes(ChangedObjectChangeGenerator.class);
for (Class extends DatabaseObject> type : types) {
ObjectQuotingStrategy quotingStrategy = diffOutputControl.getObjectQuotingStrategy();
for (Map.Entry extends DatabaseObject, ObjectDifferences> entry : diffResult.getChangedObjects(type, comparator).entrySet()) {
if (!diffResult.getReferenceSnapshot().getDatabase().isLiquibaseObject(entry.getKey()) && !diffResult.getReferenceSnapshot().getDatabase().isSystemObject(entry.getKey())) {
Change[] changes = changeGeneratorFactory.fixChanged(entry.getKey(), entry.getValue(), diffOutputControl, diffResult.getReferenceSnapshot().getDatabase(), diffResult.getComparisonSnapshot().getDatabase());
addToChangeSets(changes, changeSets, quotingStrategy, created);
}
}
}
return changeSets;
}
private List sortMissingObjects(Collection missingObjects, Database database) {
if (missingObjects.size() > 0 && supportsSortingObjects(database) && database.getConnection() != null && !(database.getConnection() instanceof OfflineConnection)) {
List schemas = new ArrayList();
for (CompareControl.SchemaComparison comparison : this.diffOutputControl.getSchemaComparisons()) {
String schemaName = comparison.getReferenceSchema().getSchemaName();
if (schemaName == null) {
schemaName = database.getDefaultSchemaName();
}
schemas.add(schemaName);
}
if (schemas.size() == 0) {
schemas.add(database.getDefaultSchemaName());
}
try {
final List dependencyOrder = new ArrayList();
DependencyUtil.NodeValueListener nameListener = new DependencyUtil.NodeValueListener() {
@Override
public void evaluating(String nodeValue) {
dependencyOrder.add(nodeValue);
}
};
DependencyUtil.DependencyGraph graph = new DependencyUtil.DependencyGraph(nameListener);
addDependencies(graph, schemas, database);
graph.computeDependencies();
if (dependencyOrder.size() > 0) {
List toSort = new ArrayList();
List toNotSort = new ArrayList();
for (DatabaseObject obj : missingObjects) {
if (!(obj instanceof Column) && obj.getSchema() != null) {
String name = obj.getSchema().getName()+"."+obj.getName();
if (dependencyOrder.contains(name)) {
toSort.add(obj);
} else {
toNotSort.add(obj);
}
} else {
toNotSort.add(obj);
}
}
Collections.sort(toSort, new Comparator() {
@Override
public int compare(DatabaseObject o1, DatabaseObject o2) {
Integer o1Order = dependencyOrder.indexOf(o1.getSchema().getName()+"."+o1.getName());
int o2Order = dependencyOrder.indexOf(o1.getSchema().getName()+"."+o2.getName());
return o1Order.compareTo(o2Order);
}
});
toSort.addAll(toNotSort);
return toSort;
}
} catch (DatabaseException e) {
LogFactory.getInstance().getLog().debug("Cannot get view dependencies: " + e.getMessage());
}
}
return new ArrayList(missingObjects);
}
/**
* Used by {@link #sortMissingObjects(Collection, Database)} to determine whether to go into the sorting logic.
*/
protected boolean supportsSortingObjects(Database database) {
return database instanceof DB2Database;
}
/**
* Adds dependencies to the graph as schema.object_name.
*/
protected void addDependencies(DependencyUtil.DependencyGraph graph, List schemas, Database database) throws DatabaseException {
if (database instanceof DB2Database) {
Executor executor = ExecutorService.getInstance().getExecutor(database);
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy