/amps-maven-plugin/src/main/java/com/atlassian/maven/plugins/amps/product/FeCruProductHandler.java
Java | 385 lines | 294 code | 58 blank | 33 comment | 17 complexity | 8f2c44cd62f43a97d5ab1da80826c2be MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
- package com.atlassian.maven.plugins.amps.product;
- import com.atlassian.maven.plugins.amps.MavenContext;
- import com.atlassian.maven.plugins.amps.MavenGoals;
- import com.atlassian.maven.plugins.amps.Node;
- import com.atlassian.maven.plugins.amps.Product;
- import com.atlassian.maven.plugins.amps.ProductArtifact;
- import com.atlassian.maven.plugins.amps.util.ConfigFileUtils.Replacement;
- import com.atlassian.maven.plugins.amps.util.ZipUtils;
- import com.atlassian.maven.plugins.amps.util.ant.AntJavaExecutorThread;
- import com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory;
- import org.apache.commons.io.FileUtils;
- import org.apache.maven.artifact.Artifact;
- import org.apache.maven.artifact.resolver.ArtifactResolver;
- import org.apache.maven.artifact.versioning.ArtifactVersion;
- import org.apache.maven.plugin.MojoExecutionException;
- import org.apache.maven.repository.RepositorySystem;
- import org.apache.tools.ant.taskdefs.Java;
- import org.apache.tools.ant.types.Path;
- import javax.annotation.Nonnull;
- import java.io.File;
- import java.io.IOException;
- import java.net.Socket;
- import java.text.DateFormat;
- import java.text.ParsePosition;
- import java.text.SimpleDateFormat;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Date;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- import static com.atlassian.maven.plugins.amps.util.ProjectUtils.createDirectory;
- import static com.atlassian.maven.plugins.amps.util.ZipUtils.unzip;
- import static com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory.output;
- import static java.lang.Thread.currentThread;
- import static java.util.Collections.singletonMap;
- import static org.apache.commons.io.FileUtils.moveToDirectory;
- /**
- * The {@link ProductHandler} for Fisheye / Crucible.
- */
- public class FeCruProductHandler extends AbstractProductHandler {
- private static final int STARTUP_CHECK_DELAY = 1000;
- private static final String FISHEYE_INST = "fisheye.inst";
- private static final String PRODUCT_GROUP_ID = "com.atlassian.crucible";
- private static final String PRODUCT_ARTIFACT_ID = "atlassian-crucible";
- private static final String TEST_DATA_GROUP_ID = "com.atlassian.fecru";
- private static final String TEST_DATA_ARTIFACT_ID = "amps-fecru";
- private static final String FECRU_TIMESTAMP_FORMAT = "yyyyMMddHHmmss";
- /**
- * Indicates whether the given version of the given artifact is an official Fecru release.
- *
- * @param version the version of the artifact
- * @param artifact the artifact
- * @return see description
- * @since 8.1.2
- */
- public static boolean isFecruRelease(final ArtifactVersion version, final Artifact artifact) {
- return isFecruArtifact(artifact) && isFecruTimestamp(version.getQualifier());
- }
- private static boolean isFecruTimestamp(final String qualifier) {
- final DateFormat dateFormat = new SimpleDateFormat(FECRU_TIMESTAMP_FORMAT);
- dateFormat.setLenient(false);
- final Date releaseDate = dateFormat.parse(qualifier, new ParsePosition(0));
- return releaseDate != null;
- }
- public static boolean isFecruArtifact(final Artifact artifact) {
- return isProductArtifact(artifact) || isTestResourcesArtifact(artifact);
- }
- public static boolean isProductArtifact(final Artifact artifact) {
- return PRODUCT_GROUP_ID.equals(artifact.getGroupId()) && PRODUCT_ARTIFACT_ID.equals(artifact.getArtifactId());
- }
- public static boolean isTestResourcesArtifact(final Artifact artifact) {
- return TEST_DATA_GROUP_ID.equals(artifact.getGroupId()) && TEST_DATA_ARTIFACT_ID.equals(artifact.getArtifactId());
- }
- private final JavaTaskFactory javaTaskFactory;
- public FeCruProductHandler(final MavenContext context, final MavenGoals goals,
- final RepositorySystem repositorySystem, final ArtifactResolver artifactResolver) {
- super(context, goals, new FeCruPluginProvider(), repositorySystem, artifactResolver);
- this.javaTaskFactory = new JavaTaskFactory(log);
- }
- @Nonnull
- @Override
- public String getId() {
- return ProductHandlerFactory.FECRU;
- }
- @Nonnull
- public ProductArtifact getArtifact() {
- return new ProductArtifact(PRODUCT_GROUP_ID, PRODUCT_ARTIFACT_ID, "RELEASE");
- }
- @Override
- public int getDefaultHttpPort() {
- return 3990;
- }
- @Override
- public int getDefaultHttpsPort() {
- return 8443;
- }
- public final void stop(@Nonnull final Product product) throws MojoExecutionException {
- final int webPort = product.getSingleNodeWebPort();
- log.info("Stopping " + product.getInstanceId() + " on ports "
- + webPort + " (http) and " + controlPort(webPort) + " (control)");
- try {
- execFishEyeCmd("stop", product, false);
- } catch (Exception e) {
- throw new MojoExecutionException(
- "Failed to stop FishEye/Crucible instance at " + product.getServer() + ":" + webPort);
- }
- waitForFishEyeToStop(product);
- }
- @Nonnull
- @Override
- protected List<Replacement> getReplacements(@Nonnull final Product product, final int nodeIndex) {
- final List<Replacement> replacements = super.getReplacements(product, nodeIndex);
- final File homeDirectory = getHomeDirectory(product);
- final int webPort = product.getSingleNodeWebPort();
- replacements.add(new Replacement("@CONTROL_BIND@", String.valueOf(controlPort(webPort))));
- replacements.add(new Replacement("@HTTP_BIND@", String.valueOf(webPort)));
- replacements.add(new Replacement("@HTTP_CONTEXT@", String.valueOf(product.getContextPath()), false));
- replacements.add(new Replacement("@HOME_DIR@", homeDirectory.getAbsolutePath()));
- replacements.add(new Replacement("@SITE_URL@", siteUrl(product)));
- return replacements;
- }
- @Nonnull
- @Override
- protected List<File> getConfigFiles(@Nonnull final Product product, @Nonnull final File homeDir) {
- final List<File> configFiles = super.getConfigFiles(product, homeDir);
- configFiles.add(new File(homeDir, "config.xml"));
- configFiles.add(new File(homeDir, "var/data/crudb/crucible.script"));
- return configFiles;
- }
- @Override
- @Nonnull
- protected File extractApplication(final Product product) throws MojoExecutionException {
- final File appDir = createDirectory(getAppDirectory(product));
- final ProductArtifact artifact = getArtifact(product);
- final File cruDistZip = goals.copyZip(getBuildDirectory(), artifact, "test-dist.zip");
- try {
- // We remove one level of root folder from the zip if present
- final int nestingLevel = ZipUtils.countNestingLevel(cruDistZip);
- unzip(cruDistZip, appDir.getPath(), nestingLevel > 0 ? 1 : 0);
- } catch (final IOException ex) {
- throw new MojoExecutionException("Unable to extract application ZIP artifact", ex);
- }
- return appDir;
- }
- protected File getAppDirectory(Product ctx) {
- return new File(getBaseDirectory(ctx), ctx.getId() + "-" + ctx.getVersion());
- }
- @Nonnull
- @Override
- public final Optional<ProductArtifact> getTestResourcesArtifact() {
- return Optional.of(new ProductArtifact(TEST_DATA_GROUP_ID, TEST_DATA_ARTIFACT_ID));
- }
- @Nonnull
- @Override
- protected Map<String, String> getSystemProperties(final Product product, final int nodeIndex) {
- return singletonMap(FISHEYE_INST, getHomeDirectory(product).getAbsolutePath());
- }
- private File getHomeDirectory(final Product product) {
- final List<File> homeDirectories = getHomeDirectories(product);
- if (homeDirectories.size() != 1) {
- throw new IllegalStateException("Expected one home directory but found " + homeDirectories);
- }
- return homeDirectories.get(0);
- }
- @Override
- @Nonnull
- protected File getBundledPluginPath(Product product, File productDir) {
- return new File(productDir, "plugins/bundled-plugins.zip");
- }
- @Nonnull
- @Override
- protected final Optional<File> getUserInstalledPluginsDirectory(final Product product, File appDir, File homeDir) {
- return Optional.of(new File(new File(new File(homeDir, "var"), "plugins"), "user"));
- }
- @Override
- protected boolean supportsStaticPlugins() {
- return false;
- }
- @Override
- @Nonnull
- protected final List<Node> startProduct(
- final Product product, final File productFile, final List<Map<String, String>> systemProperties)
- throws MojoExecutionException {
- final int webPort = product.getSingleNodeWebPort();
- log.info("Starting " + product.getInstanceId() + " on ports "
- + webPort + " (http) and " + controlPort(webPort) + " (control)");
- AntJavaExecutorThread thread;
- try {
- thread = execFishEyeCmd("run", product, true);
- } catch (Exception e) {
- throw new MojoExecutionException("Error starting fisheye.", e);
- }
- waitForFishEyeToStart(product, thread);
- return product.getNodes();
- }
- private void waitForFishEyeToStart(final Product ctx, final AntJavaExecutorThread thread)
- throws MojoExecutionException {
- boolean connected = false;
- int waited = 0;
- final int webPort = ctx.getSingleNodeWebPort();
- while (!connected) {
- try {
- Thread.sleep(STARTUP_CHECK_DELAY);
- } catch (final InterruptedException e) {
- // ignore
- currentThread().interrupt();
- }
- try {
- new Socket("localhost", webPort).close();
- connected = true;
- } catch (IOException e) {
- // ignore
- }
- if (thread.isFinished()) {
- throw new MojoExecutionException("Fisheye failed to start.", thread.getBuildException());
- }
- if (waited++ * STARTUP_CHECK_DELAY > ctx.getStartupTimeout()) {
- throw new MojoExecutionException("FishEye took longer than " + ctx.getStartupTimeout() + "ms to start!");
- }
- }
- }
- private void waitForFishEyeToStop(final Product ctx) throws MojoExecutionException {
- boolean connected = true;
- int waited = 0;
- final int webPort = ctx.getSingleNodeWebPort();
- while (connected) {
- try {
- Thread.sleep(STARTUP_CHECK_DELAY);
- } catch (InterruptedException e) {
- // ignore
- currentThread().interrupt();
- }
- try {
- new Socket("localhost", webPort).close();
- } catch (IOException e) {
- connected = false;
- }
- if (waited++ * STARTUP_CHECK_DELAY > ctx.getShutdownTimeout()) {
- throw new MojoExecutionException("FishEye took longer than " + ctx.getShutdownTimeout() + "ms to stop!");
- }
- }
- }
- private AntJavaExecutorThread execFishEyeCmd(
- final String bootCommand, final Product product, final boolean useDebugArgs) {
- final List<Map<String, String>> systemProperties = mergeSystemProperties(product);
- if (systemProperties.size() != 1) {
- // Fecru only supports single-node operation
- throw new IllegalStateException("Expected one map but found " + systemProperties);
- }
- final Java java = javaTaskFactory.newJavaTask(
- output(product.getOutput()).
- systemProperties(systemProperties.get(0)).
- jvmArgs(product.getJvmArgs() + (useDebugArgs ? product.getSingleNodeDebugArgs() : "")));
- final Path classpath = java.createClasspath();
- classpath.createPathElement().setLocation(new File(getAppDirectory(product), "fisheyeboot.jar"));
- java.setClassname("com.cenqua.fisheye.FishEyeCtl");
- java.createArg().setValue(bootCommand);
- final AntJavaExecutorThread javaThread = new AntJavaExecutorThread(java);
- javaThread.start();
- return javaThread;
- }
- protected File getBuildDirectory() {
- return new File(project.getBuild().getDirectory());
- }
- private String siteUrl(final Product ctx) {
- return "http://" + ctx.getServer() + ":" + ctx.getSingleNodeWebPort() + ctx.getContextPath();
- }
- /**
- * The control port is the HTTP port with a "1" appended to it, e.g. 3990 becomes 39901.
- *
- * @param httpPort the HTTP port
- * @return the control port
- */
- private static int controlPort(final int httpPort) {
- return httpPort * 10 + 1;
- }
- private static class FeCruPluginProvider extends AbstractPluginProvider {
- @Override
- protected Collection<ProductArtifact> getSalArtifacts(String salVersion) {
- return Arrays.asList(
- new ProductArtifact("com.atlassian.sal", "sal-api", salVersion),
- new ProductArtifact("com.atlassian.sal", "sal-fisheye-plugin", salVersion)
- );
- }
- }
- @Override
- protected void cleanupProductHomeForZip(@Nonnull final Product product, @Nonnull final File homeDirectory)
- throws MojoExecutionException, IOException {
- super.cleanupProductHomeForZip(product, homeDirectory);
- FileUtils.deleteQuietly(new File(homeDirectory, "var/log"));
- FileUtils.deleteQuietly(new File(homeDirectory, "var/plugins"));
- FileUtils.deleteQuietly(new File(homeDirectory, "cache/plugins"));
- }
- @Override
- @Nonnull
- protected String getLibArtifactTargetDir() {
- return "lib";
- }
- /**
- * Overridden as most Fecru ZIPped test data is of a different non-standard structure,
- * i.e. standard structure should have a single root folder.
- * If it finds something looking like the old structure, it will re-organize the data to match what is expected
- * of the new structure.
- *
- * @param tmpDir the temp directory
- * @param product the product
- * @return the root dir
- * @throws IOException if an I/O error occurs
- */
- @Nonnull
- @Override
- protected File getRootDir(final File tmpDir, final Product product) throws IOException {
- final File[] topLevelFiles = Optional.ofNullable(tmpDir.listFiles()).orElse(new File[0]);
- if (topLevelFiles.length == 1) {
- return topLevelFiles[0];
- }
- log.info("Non-standard zip structure identified. Assume using older non-standard FECRU zip format");
- log.info("Therefore reorganise unpacked data directories to match standard format.");
- final File tmpGen = new File(tmpDir, "generated-resources");
- final File tmpHome = new File(tmpGen, product.getId() + "-home");
- for (final File file : topLevelFiles) {
- moveToDirectory(file, tmpHome, true);
- }
- return tmpGen;
- }
- }