au.net.causal.maven.plugins.boxdb.SquirrelMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of boxdb-maven-plugin Show documentation
Show all versions of boxdb-maven-plugin Show documentation
Maven plugin to start databases using Docker and VMs
package au.net.causal.maven.plugins.boxdb;
import au.net.causal.maven.plugins.boxdb.db.BoxDatabase;
import au.net.causal.maven.plugins.boxdb.db.BoxDatabaseException;
import au.net.causal.maven.plugins.boxdb.db.BoxDatabaseFactory;
import au.net.causal.maven.plugins.boxdb.db.DatabaseTarget;
import au.net.causal.maven.plugins.boxdb.db.DockerService;
import au.net.causal.maven.plugins.boxdb.db.JdbcConnectionInfo;
import au.net.causal.maven.plugins.boxdb.db.JdbcDriverInfo;
import au.net.causal.maven.plugins.boxdb.db.RunnerDependency;
import io.fabric8.maven.docker.access.DockerAccessException;
import org.apache.commons.io.IOUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.shared.filtering.MavenFilteringException;
import org.apache.maven.shared.filtering.MavenReaderFilterRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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 javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* Runs the Squirrel database tool against the database.
*/
@Mojo(name="squirrel", requiresProject = false)
public class SquirrelMojo extends StartAndWaitMojo
{
private boolean containerWasRunningWhenMojoInvoked;
@Override
protected void executeInternal(ExceptionalSupplier dockerService)
throws DockerAccessException, MojoExecutionException
{
try
{
BoxDatabaseFactory dbFactory = databaseFactory(dockerService);
BoxDatabase db = database(dockerService);
containerWasRunningWhenMojoInvoked = db.isRunning();
JavaRunner runner = JavaRunner.createFromDependencies(
"net.sourceforge.squirrel_sql.client.Main",
Arrays.asList(
new RunnerDependency("net.sf.squirrel-sql", "squirrel-sql", "3.5.0"),
//The following 3 extra dependencies are for Java 11 compatibility - these are not in core JDK any more
new RunnerDependency("javax.xml.bind", "jaxb-api", "2.3.0"),
new RunnerDependency("org.glassfish.jaxb", "jaxb-runtime", "2.3.0.1"),
new RunnerDependency("javax.activation", "javax.activation-api", "1.2.0")),
repositorySystem, repoSession, remoteRepos, classLoaderCache
);
Path squirrelSettingsDir;
if (project == null || project.getBuild() == null || project.getBuild().getDirectory() == null)
squirrelSettingsDir = Paths.get(System.getProperty("user.home"), ".boxdb", "squirrel");
else
squirrelSettingsDir = Paths.get(project.getBuild().getDirectory()).resolve("squirrel").toAbsolutePath();
Files.createDirectories(squirrelSettingsDir);
generateSquirrelSettingsFiles(squirrelSettingsDir, db, dbFactory);
//TODO only start/stop if not already started
registerHookAndStartDatabase(dockerService);
runner.execute("--native-laf", "--user-settings-dir", squirrelSettingsDir.toString());
}
catch (BoxDatabaseException e)
{
throw new MojoExecutionException("Error setting up database: " + e, e);
}
catch (DependencyResolutionException e)
{
throw new MojoExecutionException("Error resolving Squirrel dependencies: " + e, e);
}
catch (IOException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e)
{
throw new MojoExecutionException("Error running Squirrel: " + e, e);
}
waitForControlC();
}
@Override
protected boolean shouldStopContainer()
{
if (!containerWasRunningWhenMojoInvoked)
return super.shouldStopContainer();
else
return false;
}
@Override
protected String getWaitMessage()
{
if (!containerWasRunningWhenMojoInvoked)
return "Squirrel is now running. After Squirrel is exited the database will shut down...";
else
return "Squirrel is now running.";
}
private void generateSquirrelSettingsFiles(Path dir, BoxDatabase db, BoxDatabaseFactory dbFactory)
throws IOException, MojoExecutionException, BoxDatabaseException, DependencyResolutionException
{
//Put some skeleton files in the squirrel directory
//Prefs disabled auto-update and the initial welcome screen
copyResource("prefs.xml", dir);
//Drivers has JDBC driver configuration
generateDriverConfiguration(dir, db, dbFactory);
//Aliases has database instance details
generateAliasConfiguration(dir, db, dbFactory);
}
private void copyResource(String name, Path targetDir)
throws IOException
{
URL resourceUrl = SquirrelMojo.class.getResource("squirrel/" + name);
if (resourceUrl == null)
throw new FileNotFoundException("Resource " + name + " not found.");
Path targetFile = targetDir.resolve(name);
//If file is present from last run then leave that one - window positions, etc. are database independent
if (Files.exists(targetFile))
return;
try (InputStream is = resourceUrl.openStream())
{
Files.copy(is, targetFile);
}
}
private void generateAliasConfiguration(Path targetDir, BoxDatabase db, BoxDatabaseFactory dbFactory)
throws IOException, BoxDatabaseException, MojoExecutionException
{
Path targetFile = targetDir.resolve("SQLAliases23.xml");
URL resourceUrl = SquirrelMojo.class.getResource("squirrel/" + dbFactory.name() + "/SQLAliases23.xml");
if (resourceUrl == null)
resourceUrl = SquirrelMojo.class.getResource("squirrel/SQLAliases23.xml");
if (resourceUrl == null)
throw new FileNotFoundException("Alias resource not found.");
Properties extraProperties = new Properties();
box.toProperties(extraProperties);
JdbcConnectionInfo jdbcInfo = db.jdbcConnectionInfo(DatabaseTarget.USER);
extraProperties.setProperty("jdbc.url", jdbcInfo.getUri());
if (jdbcInfo.getUser() != null)
extraProperties.setProperty("jdbc.user", jdbcInfo.getUser());
if (jdbcInfo.getPassword() != null)
extraProperties.setProperty("jdbc.password", jdbcInfo.getPassword());
JdbcConnectionInfo adminJdbcInfo = db.jdbcConnectionInfo(DatabaseTarget.ADMIN);
extraProperties.setProperty("admin.jdbc.url", adminJdbcInfo.getUri());
if (adminJdbcInfo.getUser() != null)
extraProperties.setProperty("admin.jdbc.user", adminJdbcInfo.getUser());
if (adminJdbcInfo.getPassword() != null)
extraProperties.setProperty("admin.jdbc.password", adminJdbcInfo.getPassword());
try (Reader reader = new InputStreamReader(resourceUrl.openStream(), StandardCharsets.UTF_8);
Writer writer = Files.newBufferedWriter(targetFile, StandardCharsets.UTF_8))
{
MavenReaderFilterRequest filterRequest = new MavenReaderFilterRequest(reader, true, project,
Collections.emptyList(), true, session,
extraProperties);
try (Reader filteredReader = readerFilter.filter(filterRequest))
{
IOUtils.copy(filteredReader, writer);
}
catch (MavenFilteringException e)
{
throw new MojoExecutionException("Error generating alias resource: " + e, e);
}
}
}
private void generateDriverConfiguration(Path targetDir, BoxDatabase db, BoxDatabaseFactory dbFactory)
throws IOException, MojoExecutionException, BoxDatabaseException, DependencyResolutionException
{
Path targetFile = targetDir.resolve("SQLDrivers.xml");
URL resourceUrl = SquirrelMojo.class.getResource("squirrel/SQLDrivers.xml");
if (resourceUrl == null)
throw new FileNotFoundException("Drivers resource not found.");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true); //Even though our source XML has no namespaces, let's still have this ability in dbf
TransformerFactory tf = TransformerFactory.newInstance();
XPathFactory xpf = XPathFactory.newInstance();
try
{
DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
Transformer transformer = tf.newTransformer();
XPath xPath = xpf.newXPath();
try (InputStream is = resourceUrl.openStream(); OutputStream os = Files.newOutputStream(targetFile))
{
Document doc = documentBuilder.parse(is, resourceUrl.toExternalForm());
//Fill in jarFileNames of JDBC driver
Element jarFileNames = (Element)xPath.evaluate("//jarFileNames", doc, XPathConstants.NODE);
for (Path driverJarFile : resolveJdbcDriverJarFiles(db))
{
//For each driver add something like this
//
// ${db.jar}
//
Element bean = doc.createElement("Bean");
bean.setAttribute("Class", "net.sourceforge.squirrel_sql.fw.util.beanwrapper.StringWrapper");
jarFileNames.appendChild(bean);
Element stringElement = doc.createElement("string");
bean.appendChild(stringElement);
String jar = driverJarFile.toAbsolutePath().toString();
stringElement.setTextContent(jar);
}
//Fill in other details
Element nameElement = (Element)xPath.evaluate("//name", doc, XPathConstants.NODE);
nameElement.setTextContent(dbFactory.name());
Element driverClassNameElement = (Element)xPath.evaluate("//driverClassName", doc, XPathConstants.NODE);
driverClassNameElement.setTextContent(db.jdbcDriverInfo().getDriverClassName());
transformer.transform(new DOMSource(doc), new StreamResult(os));
}
}
catch (XPathException | TransformerException | ParserConfigurationException | SAXException e)
{
throw new MojoExecutionException("Error generating drivers file: " + e, e);
}
}
private List resolveJdbcDriverJarFiles(BoxDatabase db)
throws DependencyResolutionException, BoxDatabaseException
{
JdbcDriverInfo jdbcDriverInfo = db.jdbcDriverInfo();
try
{
return DependencyUtils.resolveDependencies(jdbcDriverInfo.getDependencies(), repositorySystem, repoSession, remoteRepos);
}
catch (DependencyResolutionException e)
{
//If a download URL exists for the drivers, print out helpful message to user first
if (jdbcDriverInfo.getDownloadUrl() != null)
{
StringBuilder installCommands = new StringBuilder();
for (RunnerDependency dependency : jdbcDriverInfo.getDependencies())
{
installCommands.append("mvn install:install-file -DgroupId=" + dependency.getGroupId() +
" -DartifactId=" + dependency.getArtifactId() +
" -Dversion=" + dependency.getVersion() +
" -Dpackaging=" + dependency.getType() +
(dependency.getClassifier() != null ? " -Dclassifier=" + dependency.getClassifier() : "") +
" -DgeneratePom=true -Dfile=\n");
}
getLog().error("Database drivers could not be found in the Maven repository. You can download them from:\n" +
jdbcDriverInfo.getDownloadUrl() + "\n" +
" and install them to your local repository with the following command:\n\n" +
installCommands);
}
throw e;
}
}
}