All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.net.causal.maven.plugins.boxdb.SquirrelMojo Maven / Gradle / Ivy

There is a newer version: 3.3
Show newest version
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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy