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

com.jd.blockchain.maven.plugins.contract.AbstractContractMojo Maven / Gradle / Ivy

The newest version!
package com.jd.blockchain.maven.plugins.contract;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import java.util.Set;

import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter;
import org.apache.maven.shared.utils.PropertyUtils;
import org.apache.maven.shared.utils.StringUtils;

import com.jd.blockchain.contract.archiver.Archive;
import com.jd.blockchain.contract.archiver.CarArchiver;
import com.jd.blockchain.contract.archiver.CodeSettings;
import com.jd.blockchain.contract.archiver.LibArchiver;
import com.jd.blockchain.maven.plugins.contract.analysis.DefaultCodeAnalyzer;

/**
 * Base class for creating a contract package from project classes.
 * 
 * @author huanghaiquan
 *
 */
public abstract class AbstractContractMojo extends AbstractMojo {

	private static final String[] DEFAULT_EXCLUDES = new String[] { "**/package.html" };

	private static final String[] DEFAULT_INCLUDES = new String[] { "**/**" };

	private static final String SYSTEM_EXCLUDE_GROUP_IDS_RESOURCE = "/exclude.group.ids";

	private static final String SYSTEM_EXCLUDE_ARTIFACT_IDS_RESOURCE = "/exclude.artifact.ids";

	/**
	 * List of files to include. Specified as fileset patterns which are relative to
	 * the input directory whose contents is being packaged into the CAR.
	 */
	@Parameter
	private String[] includes;

	/**
	 * List of files to exclude. Specified as fileset patterns which are relative to
	 * the input directory whose contents is being packaged into the CAR.
	 */
	@Parameter
	private String[] excludes;

	/**
	 * Directory containing the generated CAR file.
	 */
	@Parameter(defaultValue = "${project.build.directory}", required = true)
	private File outputDirectory;

	/**
	 * Name of the generated CAR file.
	 */
	@Parameter(defaultValue = "${project.build.finalName}", readonly = true)
	private String finalName;

	/**
	 * The {@link {MavenProject}.
	 */
	@Parameter(defaultValue = "${project}", readonly = true, required = true)
	private MavenProject project;

	/**
	 * The {@link MavenSession}.
	 */
	@Parameter(defaultValue = "${session}", readonly = true, required = true)
	private MavenSession session;

	@Component
	private MavenProjectHelper projectHelper;

	/**
	 * Timestamp for reproducible output archive entries, either formatted as ISO
	 * 8601 yyyy-MM-dd'T'HH:mm:ssXXX or as an int representing seconds
	 * since the epoch (like SOURCE_DATE_EPOCH).
	 *
	 * @since 3.2.0
	 */
	@Parameter(defaultValue = "${project.build.outputTimestamp}")
	private String outputTimestamp;

	/**
	 * Whether output the dependencies into the single CLIB file, to shrink the size
	 * of CAR file;
	 */
	@Parameter(property = "outputLibrary", defaultValue = "false")
	private boolean outputLibrary;

	/**
	 * Max size of the CAR file.
	 */
	@Parameter(property = "maxCarSize", defaultValue = "1")
	protected int maxCarSize;

	@Parameter(property = "maxCarSizeUnit", defaultValue = "MB")
	protected ArchiveSizeUnit maxCarSizeUnit;

	/**
	 * Comma(,) separated list of Artifact names to exclude.
	 *
	 */
	@Parameter(property = "excludeArtifactIds", defaultValue = "")
	protected String excludeArtifactIds;

	/**
	 * Comma(,) separated list of Artifact names to include. Empty String indicates
	 * include everything (default).
	 *
	 */
	@Parameter(property = "includeArtifactIds", defaultValue = "")
	protected String includeArtifactIds;

	/**
	 * Comma(,) separated list of GroupId Names to exclude.
	 *
	 */
	@Parameter(property = "excludeGroupIds", defaultValue = "")
	protected String excludeGroupIds;

	/**
	 * Comma(,) separated list of GroupIds to include. Empty String indicates
	 * include everything (default).
	 *
	 */
	@Parameter(property = "includeGroupIds", defaultValue = "")
	protected String includeGroupIds;
	
	
	private CodeAnalyzer codeAnalyzer;

	/**
	 * @return the {@link #project}
	 */
	protected final MavenProject getProject() {
		return project;
	}

	/**
	 * Return the specific output directory to serve as the root for the archive.
	 * 
	 * @return get classes directory.
	 */
	protected abstract File getClassesDirectory();
	
	
	public AbstractContractMojo() {
		codeAnalyzer = new DefaultCodeAnalyzer(getLog());
	}

	/**
	 * Generates the CONTRACT.
	 * 
	 * @throws MojoExecutionException in case of an error.
	 *
	 */
	@Override
	public void execute() throws MojoExecutionException {
		// abort if empty;
		if (!getClassesDirectory().exists() || getClassesDirectory().list().length < 1) {
			throw new MojoExecutionException("The contract codes is empty! -- No content was marked for inclusion!");
		}

		// settings about contract code;
		CodeSettings codeSettings = getCodeSettings();

		// libraries of dependencies;
		Set libraries = getDependencies();
		
		// code analysis;
		AnalysisResult analysisResult = codeAnalyzer.analyze(codeSettings.getCodebaseDirectory(), libraries);
		
		codeSettings.setDeclaringInterface(analysisResult.getDeclaringInterface());
		codeSettings.setImplementClass(analysisResult.getImplementClass());
		codeSettings.addExcludes(analysisResult.getExcludes());
		
		libraries = analysisResult.getLibraries();

		// package;
		Archive car = createCarArchive(codeSettings, libraries);

		Archive lib = null;
		if (outputLibrary && libraries.size() > 0) {
			lib = createLibArchive(libraries);
		}

		// attach archives;
		if (hasArtifact(getProject())) {
			throw new MojoExecutionException("The current project has already set an artifact! "
					+ "Cannot attach the generated " + car.getType() + " artifact to the project to replace them.");
		}
		getProject().getArtifact().setFile(car.getFile());

		if (lib != null) {
			projectHelper.attachArtifact(getProject(), car.getType(), lib.getType(), lib.getFile());
		}
	}

	/**
	 * Returns the contract file to generate, based on an optional classifier.
	 *
	 * @param basedir         the output directory
	 * @param resultFinalName the name of the ear file
	 * @return the file to generate
	 */
	private File getOutputFile(File basedir, String finalName, String extensionName) {
		if (basedir == null) {
			throw new IllegalArgumentException("basedir is not allowed to be null");
		}
		if (finalName == null) {
			throw new IllegalArgumentException("finalName is not allowed to be null");
		}

		StringBuilder fileName = new StringBuilder(finalName);

		fileName.append("." + extensionName);

		return new File(basedir, fileName.toString());
	}

	/**
	 * Generates the CAR file.
	 * 
	 * @return The instance of File for the created archive file.
	 * @throws MojoExecutionException in case of an error.
	 */
	private Archive createCarArchive(CodeSettings codeSettings, Set libraries)
			throws MojoExecutionException {
		try {
			File carFile = getOutputFile(outputDirectory, finalName, CarArchiver.TYPE);

			CarArchiver carArchiver = new CarArchiver(carFile, codeSettings, libraries);
			carArchiver.setIncludedLibraries(!outputLibrary);
			carArchiver.setCreatedBy(getCreatedBy());

			return carArchiver.createArchive();
		} catch (Exception e) {
			throw new MojoExecutionException("Error occurred while generating CAR archive! --" + e.getMessage(), e);
		}
	}

	private CodeSettings getCodeSettings() throws MojoExecutionException {
		File classesDirectory = getClassesDirectory();
		if (!classesDirectory.exists()) {
			throw new MojoExecutionException("The contract codes is empty! -- No content was marked for inclusion!");
		}

		CodeSettings codeSettings = new CodeSettings(classesDirectory);
		codeSettings.addIncludes(getIncludes());
		codeSettings.addExcludes(getExcludes());

		return codeSettings;
	}

	/**
	 * Generates the CLIB file.
	 * 
	 * @return The instance of File for the created archive file.
	 * @throws MojoExecutionException in case of an error.
	 */
	private Archive createLibArchive(Set libraries) throws MojoExecutionException {

		File clibFile = getOutputFile(outputDirectory, finalName, LibArchiver.TYPE);

		LibArchiver clibArchiver = new LibArchiver(clibFile, libraries);
		clibArchiver.setCreatedBy(getCreatedBy());

		return clibArchiver.createArchive();
	}

	protected Set getDependencies() throws MojoExecutionException {
		// add filters in well known order, least specific to most specific
		FilterArtifacts filter = new FilterArtifacts();

//		filter.addFilter(new ProjectTransitivityFilter(getProject().getDependencyArtifacts(), this.excludeTransitive));
//		
//		filter.addFilter(new ScopeFilter(cleanToBeTokenizedString(this.includeScope),
//				cleanToBeTokenizedString(this.excludeScope)));
//
//		filter.addFilter(new TypeFilter(cleanToBeTokenizedString(this.includeTypes),
//				cleanToBeTokenizedString(this.excludeTypes)));
//
//		filter.addFilter(new ClassifierFilter(cleanToBeTokenizedString(this.includeClassifiers),
//				cleanToBeTokenizedString(this.excludeClassifiers)));

		GroupIdFilter systemGroupIdFilter = new GroupIdFilter(null,
				cleanToBeTokenizedString(getSystemExcludeGroupIds()));
		filter.addFilter(systemGroupIdFilter);

		GroupIdFilter userGroupIdFilter = new GroupIdFilter(cleanToBeTokenizedString(this.includeGroupIds),
				cleanToBeTokenizedString(this.excludeGroupIds));
		filter.addFilter(userGroupIdFilter);

		ArtifactIdFilter systemArtifactIdFilter = new ArtifactIdFilter(null,
				cleanToBeTokenizedString(getSystemExcludeArtifactIds()));
		filter.addFilter(systemArtifactIdFilter);

		ArtifactIdFilter userArtifactIdFilter = new ArtifactIdFilter(cleanToBeTokenizedString(this.includeArtifactIds),
				cleanToBeTokenizedString(this.excludeArtifactIds));
		filter.addFilter(userArtifactIdFilter);

		Set artifacts = getProject().getDependencyArtifacts();

		if (getLog().isDebugEnabled()) {
			getLog().debug("-------- All Dependencies[" + artifacts.size() + "] --------");
			int i = 0;
			for (Artifact artifact : artifacts) {
				getLog().debug(String.format("%s-- %s [%s]", i, artifact.toString(), artifact.getFile().getName()));
				i++;
			}
			getLog().debug("----------------------------------");
		}

		try {
			getLog().debug("filter dependencies...");
			artifacts = filter.filter(artifacts);
		} catch (ArtifactFilterException e) {
			throw new MojoExecutionException(e.getMessage(), e);
		}

		if (getLog().isDebugEnabled()) {
			getLog().debug("-------- Exporting Dependencies[" + artifacts.size() + "] --------");
			int i = 0;
			for (Artifact artifact : artifacts) {
				getLog().debug(String.format("%s-- %s [%s]", i, artifact.toString(), artifact.getFile().getName()));
				i++;
			}
			getLog().debug("----------------------------------");
		}

		return artifacts;
	}

	private String getSystemExcludeGroupIds() throws MojoExecutionException {
		try {
			try (InputStream in = this.getClass().getResourceAsStream(SYSTEM_EXCLUDE_GROUP_IDS_RESOURCE)) {
				return readText(in, "UTF-8");
			}
		} catch (IOException e) {
			throw new MojoExecutionException(e.getMessage(), e);
		}
	}

	private String getSystemExcludeArtifactIds() throws MojoExecutionException {
		try {
			try (InputStream in = this.getClass().getResourceAsStream(SYSTEM_EXCLUDE_ARTIFACT_IDS_RESOURCE)) {
				return readText(in, "UTF-8");
			}
		} catch (IOException e) {
			throw new MojoExecutionException(e.getMessage(), e);
		}
	}

	/**
	 * Detects the project has already set an artifact;
	 * 
	 * @param prj
	 * @return
	 */
	private boolean hasArtifact(MavenProject prj) {
		if (prj.getArtifact().getFile() != null) {
			return prj.getArtifact().getFile().isFile();
		} else {
			return false;
		}
	}

	private static String getCreatedBy() {
		String createdBy = ContractMavenPlugin.GROUP_ID + ":" + ContractMavenPlugin.ARTIFACT_ID;
		String version = getCreatedByVersion(ContractMavenPlugin.GROUP_ID, ContractMavenPlugin.ARTIFACT_ID);
		if (version != null) {
			createdBy = createdBy + ":" + version;
		}
		return createdBy;
	}

	static String getCreatedByVersion(String groupId, String artifactId) {
		final Properties properties = PropertyUtils.loadOptionalProperties(MavenArchiver.class
				.getResourceAsStream("/META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties"));

		return properties.getProperty("version");
	}

	/**
	 * The included classes;
	 * 
	 * @return
	 */
	protected String[] getIncludes() {
		if (includes != null && includes.length > 0) {
			return includes;
		}
		return DEFAULT_INCLUDES;
	}

	/**
	 * The excluded classes;
	 * 
	 * @return
	 */
	protected String[] getExcludes() {
		if (excludes != null && excludes.length > 0) {
			return excludes;
		}
		return DEFAULT_EXCLUDES;
	}

	/**
	 * clean up configuration string before it can be tokenized
	 * 
	 * @param str The str which should be cleaned.
	 * @return cleaned up string.
	 */
	public static String cleanToBeTokenizedString(String str) {
		String ret = "";
		if (!StringUtils.isEmpty(str)) {
			// remove initial and ending spaces, plus all spaces next to commas
			ret = str.trim().replaceAll("[\\s]*,[\\s]*", ",");
		}

		return ret;
	}

	/**
	 * 从流读取文本;
	 * 
	 * @param in      in
	 * @param charset charset
	 * @return String
	 * @throws IOException exception
	 */
	private static String readText(InputStream in, String charset) throws IOException {
		InputStreamReader reader = new InputStreamReader(in, charset);
		try {
			StringBuilder content = new StringBuilder();
			char[] buffer = new char[64];
			int len = 0;
			while ((len = reader.read(buffer)) > 0) {
				content.append(buffer, 0, len);
			}
			return content.toString();
		} finally {
			reader.close();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy