Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use LiquibaseMojo as implemention class. Include Pro commands in plugin.xml DAT-12158 #12

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions src/main/java/liquibase/sdk/maven/plugins/BuildPluginXmlMojo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package liquibase.sdk.maven.plugins;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.SortedMap;
import java.util.SortedSet;

@Mojo(name = "build-plugin-xml")
public class BuildPluginXmlMojo extends AbstractMojo {
Comment on lines +27 to +28

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some javadoc to this class that explains what it does.


@Parameter(required = true)
private File liquibaseClassesDir;

@Parameter(required = false)
private File liquibaseProClassesDir;

@Parameter(required = true)
private File outputFile;

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
if (!liquibaseClassesDir.exists()) {
throw new MojoFailureException(liquibaseClassesDir.getAbsolutePath() + " does not exist");
}

Document pluginXml = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element pluginElement = pluginXml.createElement("plugin");
pluginXml.appendChild(pluginElement);

addNode("name", "liquibase-maven-plugin", pluginElement, pluginXml);
addNode("description", "A Maven plugin wraps up some of the functionality of Liquibase", pluginElement, pluginXml);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should change the description now:

Suggested change
addNode("description", "A Maven plugin wraps up some of the functionality of Liquibase", pluginElement, pluginXml);
addNode("description", "A Maven plugin wraps up the functionality of Liquibase", pluginElement, pluginXml);

addNode("groupId", "org.liquibase", pluginElement, pluginXml);
addNode("artifactId", "liquibase-maven-plugin", pluginElement, pluginXml);
addNode("version", "0-SNAPSHOT", pluginElement, pluginXml);
addNode("goalPrefix", "liquibase", pluginElement, pluginXml);
addNode("isolatedRealm", "false", pluginElement, pluginXml);
addNode("inheritedByDefault", "true", pluginElement, pluginXml);

Element mojosElement = pluginXml.createElement("mojos");
pluginElement.appendChild(mojosElement);

URLClassLoader classloader;
if (liquibaseProClassesDir != null && liquibaseProClassesDir.exists()) {
classloader =
new URLClassLoader(new URL[]{
liquibaseClassesDir.toURI().toURL(),
liquibaseProClassesDir.toURI().toURL()
});
} else {
classloader =
new URLClassLoader(new URL[]{
liquibaseClassesDir.toURI().toURL()
});

}

Thread.currentThread().setContextClassLoader(classloader);
Object scope = classloader.loadClass("liquibase.Scope")
.getMethod("getCurrentScope").invoke(null);

Class<?> commandFactoryClass = classloader.loadClass("liquibase.command.CommandFactory");

Object commandFactory = scope.getClass().getMethod("getSingleton", Class.class).invoke(scope, commandFactoryClass);

SortedSet commands = (SortedSet) commandFactory.getClass().getMethod("getCommands", boolean.class).invoke(commandFactory, false);
for (Object commandDef : commands) {
String[] commandName = (String[]) commandDef.getClass().getMethod("getName").invoke(commandDef);
System.out.println("See command " + StringUtils.join(commandName, " "));

Element mojoElement = pluginXml.createElement("mojo");
mojosElement.appendChild(mojoElement);

for (int i = 0; i < commandName.length; i++) {
if (i > 0) {
commandName[i] = commandName[i].substring(0, 1).toUpperCase() + commandName[i].substring(1);
}
}
String finalName = StringUtils.join(commandName);

addNode("goal", finalName, mojoElement, pluginXml);
addNode("description", (String) commandDef.getClass().getMethod("getLongDescription").invoke(commandDef), mojoElement, pluginXml);
addNode("requiresDependencyResolution", "test", mojoElement, pluginXml);
addNode("requiresDirectInvocation", "false", mojoElement, pluginXml);
addNode("requiresProject", "true", mojoElement, pluginXml);
addNode("requiresReports", "false", mojoElement, pluginXml);
addNode("aggregator", "false", mojoElement, pluginXml);
addNode("requiresOnline", "false", mojoElement, pluginXml);
addNode("inheritedByDefault", "true", mojoElement, pluginXml);
addNode("implementation", "org.liquibase.maven.plugins.LiquibaseMojo", mojoElement, pluginXml);
addNode("language", "java", mojoElement, pluginXml);
addNode("instantiationStrategy", "per-lookup", mojoElement, pluginXml);
addNode("executionStrategy", "once-per-session", mojoElement, pluginXml);
addNode("threadSafe", "false", mojoElement, pluginXml);
addNode("configurator", "map-oriented", mojoElement, pluginXml);

Element parametersElement = pluginXml.createElement("parameters");
mojoElement.appendChild(parametersElement);

Element configurationElement = pluginXml.createElement("configuration");
mojoElement.appendChild(configurationElement);

addMavenProperty("mojoExecution", "org.apache.maven.plugin.MojoExecution", "${mojoExecution}", parametersElement, configurationElement, pluginXml);
addMavenProperty("session", "org.apache.maven.execution.MavenSession", "${session}", parametersElement, configurationElement, pluginXml);
addMavenProperty("project", "org.apache.maven.project.MavenProject", "${project}", parametersElement, configurationElement, pluginXml);


SortedMap arguments = (SortedMap) commandDef.getClass().getMethod("getArguments").invoke(commandDef);
for (Object argDef : arguments.values()) {
Element parameterElement = pluginXml.createElement("parameter");
parametersElement.appendChild(parameterElement);

String argName = (String) argDef.getClass().getMethod("getName").invoke(argDef);
String dataType = ((Class) argDef.getClass().getMethod("getDataType").invoke(argDef)).getName();

addNode("name", argName, parameterElement, pluginXml);
addNode("type", dataType, parameterElement, pluginXml);
addNode("required", String.valueOf(argDef.getClass().getMethod("isRequired").invoke(argDef)), parameterElement, pluginXml);
addNode("editable", "true", parameterElement, pluginXml);
addNode("description", (String) argDef.getClass().getMethod("getDescription").invoke(argDef), parameterElement, pluginXml);

Element confElement = pluginXml.createElement(argName);
configurationElement.appendChild(confElement);
confElement.setAttribute("implementation", dataType);
confElement.setTextContent("${liquibase.command." + StringUtils.join(commandName, ".") + "." + argName + "}");
String defaultValue = (String) argDef.getClass().getMethod("getDefaultValueDescription").invoke(argDef);
if (defaultValue != null) {
confElement.setAttribute("default-value", defaultValue);
}
}
}

writeXml(pluginXml);
} catch (Exception e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}

System.out.println("GENERATE PLUGIN.XML");
}

private void addMavenProperty(String name, String type, String defaultValue, Element parametersElement, Element configurationElement, Document pluginXml) {
Element thisParameterElement = pluginXml.createElement("parameter");
parametersElement.appendChild(thisParameterElement);
addNode("name", name, thisParameterElement, pluginXml);
addNode("type", type, thisParameterElement, pluginXml);
addNode("required", "true", thisParameterElement, pluginXml);
addNode("editable", "false", thisParameterElement, pluginXml);

Element thisConfigElement = pluginXml.createElement(name);
configurationElement.appendChild(thisConfigElement);
thisConfigElement.setAttribute("implementation", type);
thisConfigElement.setAttribute("default-value", defaultValue);

}

private void writeXml(Document pluginXml) throws IOException, TransformerException {
DOMSource domSource = new DOMSource(pluginXml);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
factory.setAttribute("indent-number", 4);

transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
try (FileWriter fileWriter = new FileWriter(outputFile)) {
StreamResult sr = new StreamResult(fileWriter);
transformer.transform(domSource, sr);
}
}

private Element addNode(String nodeName, String text, Element parent, Document document) {
Element newElement = document.createElement(nodeName);
newElement.setTextContent(text);
parent.appendChild(newElement);

return newElement;
}
}