org.basinmc.maven.plugins.minecraft.AbstractArtifactMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.basinmc.maven.plugins.minecraft.AbstractArtifactMojo.java

Source

/*
 * Copyright 2016 Johannes Donath <johannesd@torchmind.com>
 * and other copyright owners as documented in the project's IP log.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.basinmc.maven.plugins.minecraft;

import com.google.common.io.ByteStreams;

import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.installer.ArtifactInstallationException;
import org.apache.maven.artifact.installer.ArtifactInstaller;
import org.apache.maven.artifact.metadata.ArtifactMetadata;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.project.artifact.ProjectArtifactMetadata;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Optional;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.WillNotClose;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Provides a basic abstract Maven Mojo implementation with utility methods for interacting with
 * Maven artifacts.
 *
 * @author <a href="mailto:johannesd@torchmind.com">Johannes Donath</a>
 */
@Immutable
@ThreadSafe
public abstract class AbstractArtifactMojo extends AbstractMinecraftMojo {
    protected static final String BASE_GROUP_ID = "org.basinmc";

    protected static final String MINECRAFT_GROUP_ID = BASE_GROUP_ID + ".minecraft";
    protected static final String SRG_ARTIFACT_ID = "mappings-srg";
    protected static final String MCP_ARTIFACT_ID = "mappings-mcp";
    protected static final String MCP_LIVE_VERSION = "0.0.0-SNAPSHOT";
    protected static final String VANILLA_CLASSIFIER = "vanilla";
    protected static final String MAPPED_CLASSIFIER = "mapped";
    protected static final String SOURCE_CLASSIFIER = "source";

    private static final Duration SNAPSHOT_CACHING_DURATION = Duration.ofHours(12);

    // <editor-fold desc="Maven Components">
    @Component
    private ArtifactFactory artifactFactory;
    @Component
    private ArtifactInstaller artifactInstaller;
    @Component
    private ArtifactResolver artifactResolver;
    // </editor-fold>

    // <editor-fold desc="Component Getters">
    @Nonnull
    protected ArtifactFactory getArtifactFactory() {
        return this.artifactFactory;
    }

    @Nonnull
    protected ArtifactInstaller getArtifactInstaller() {
        return this.artifactInstaller;
    }

    @Nonnull
    protected ArtifactResolver getArtifactResolver() {
        return this.artifactResolver;
    }
    // </editor-fold>

    /**
     * Creates an artifact with the supplied coordinates.
     */
    @Nonnull
    protected Artifact createArtifact(@Nonnull String groupId, @Nonnull String artifactId, @Nonnull String version,
            @Nonnull String type, @Nonnull String classifier) {
        return this.getArtifactFactory().createArtifactWithClassifier(groupId, artifactId, version, type,
                classifier);
    }

    /**
     * Creates an artifact with the supplied coordinates.
     */
    @Nonnull
    protected Artifact createArtifact(@Nonnull String groupId, @Nonnull String artifactId, @Nonnull String version,
            @Nonnull String type) {
        return this.getArtifactFactory().createBuildArtifact(groupId, artifactId, version, type);
    }

    /**
     * Creates an artifact with the supplied coordinate and its type set to "jar".
     */
    protected Artifact createArtifactWithClassifier(@Nonnull String groupId, @Nonnull String artifactId,
            @Nonnull String version, @Nonnull String classifier) {
        return this.createArtifact(groupId, artifactId, version, "jar", classifier);
    }

    /**
     * Fetches any resource from a remote HTTP server and stores it in a specified file.
     */
    protected void fetch(@Nonnull String url, @Nonnull Path target) throws IOException {
        this.fetch(new URL(url), target);
    }

    /**
     * Fetches any resource from a remote HTTP server and stores it in a specified file.
     */
    protected void fetch(@Nonnull URL url, @Nonnull Path target) throws IOException {
        try {
            this.fetch(url.toURI(), target);
        } catch (URISyntaxException ex) {
            throw new IOException("Invalid resource URI: " + ex.getMessage(), ex);
        }
    }

    /**
     * Fetches any resources from a remote HTTP server and stores it in a specified file.
     */
    protected void fetch(@Nonnull URI uri, @Nonnull Path target) throws IOException {
        this.fetch(uri, FileChannel.open(target, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
                StandardOpenOption.WRITE));
    }

    /**
     * Fetches any resource from a remote HTTP server and writes it to a supplied output stream.
     */
    protected void fetch(@Nonnull URI uri, @Nonnull @WillNotClose OutputStream outputStream) throws IOException {
        this.fetch(uri, Channels.newChannel(outputStream));
    }

    /**
     * Fetches any resource from a remote HTTP server and writes it to a supplied channel.
     */
    protected void fetch(@Nonnull URI uri, @Nonnull @WillNotClose WritableByteChannel outputChannel)
            throws IOException {
        HttpClient client = HttpClients.createMinimal();

        HttpGet request = new HttpGet(uri);
        HttpResponse response = client.execute(request);

        StatusLine line = response.getStatusLine();

        if (line.getStatusCode() != 200) {
            throw new IOException(
                    "Unexpected status code: " + line.getStatusCode() + " - " + line.getReasonPhrase());
        }

        try (InputStream inputStream = response.getEntity().getContent()) {
            try (ReadableByteChannel inputChannel = Channels.newChannel(inputStream)) {
                ByteStreams.copy(inputChannel, outputChannel);
            }
        }
    }

    /**
     * Attempts to locate an artifact within the local Maven repository.
     */
    @Nonnull
    protected Optional<Path> findArtifact(Artifact artifact) throws ArtifactResolutionException {
        try {
            this.getArtifactResolver().resolve(artifact, Collections.emptyList(),
                    this.getSession().getLocalRepository());
            return Optional.of(artifact.getFile().toPath());
        } catch (ArtifactNotFoundException ex) {
            return Optional.empty();
        }
    }

    /**
     * Retrieves a human readable set of artifact coordinates.
     */
    @Nonnull
    protected String getArtifactCoordinateString(@Nonnull Artifact artifact) {
        return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() + ":"
                + artifact.getType() + (artifact.hasClassifier() ? ":" + artifact.getClassifier() : "");
    }

    /**
     * Installs a passed artifact into the local repository.
     */
    protected void installArtifact(@Nonnull Artifact artifact, @Nonnull Path modelPath, @Nonnull Path artifactPath)
            throws ArtifactInstallationException {
        this.getLog().debug("Installing artifact " + this.getArtifactCoordinateString(artifact));

        ArtifactMetadata metadata = new ProjectArtifactMetadata(artifact, modelPath.toFile());
        artifact.addMetadata(metadata);

        this.getArtifactInstaller().install(artifactPath.toFile(), artifact,
                this.getSession().getLocalRepository());
    }

    /**
     * Checks whether a snapshot artifact is considered valid.
     */
    protected boolean isSnapshotArtifactValid(@Nonnull Artifact artifact, @Nullable Path path) {
        try {
            return !artifact.isSnapshot() || (path != null && Files.getLastModifiedTime(path).toInstant()
                    .isAfter(Instant.now().minus(SNAPSHOT_CACHING_DURATION)));
        } catch (IOException ex) {
            this.getLog().warn("Could not verify state of snapshot artifact "
                    + this.getArtifactCoordinateString(artifact) + ": " + ex.getMessage());
            return false;
        }
    }
}