| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The ASF licenses this file to you 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.apache.storm.maven.plugin.versioninfo; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.text.DateFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.TimeZone; |
| import org.apache.maven.model.FileSet; |
| import org.apache.maven.plugin.AbstractMojo; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.storm.maven.plugin.util.CommandExec; |
| import org.codehaus.plexus.util.FileUtils; |
| |
| @Mojo(name = "version-info") |
| public class VersionInfoMojo extends AbstractMojo { |
| |
| @Parameter(defaultValue = "${project}") |
| private MavenProject project; |
| |
| @Parameter(required = true) |
| private FileSet source; |
| |
| @Parameter(defaultValue = "version-info.build.time") |
| private String buildTimeProperty; |
| |
| @Parameter(defaultValue = "version-info.source.md5") |
| private String md5Property; |
| |
| @Parameter(defaultValue = "version-info.scm.uri") |
| private String scmUriProperty; |
| |
| @Parameter(defaultValue = "version-info.scm.branch") |
| private String scmBranchProperty; |
| |
| @Parameter(defaultValue = "version-info.scm.commit") |
| private String scmCommitProperty; |
| |
| @Parameter(defaultValue = "git") |
| private String gitCommand; |
| |
| @Parameter(defaultValue = "svn") |
| private String svnCommand; |
| private List<String> scmOut; |
| |
| @SuppressWarnings("rawtypes") |
| private static String getCommaSeparatedList(List list) { |
| StringBuilder buffer = new StringBuilder(); |
| String separator = ""; |
| for (Object e : list) { |
| buffer.append(separator).append(e); |
| separator = ","; |
| } |
| return buffer.toString(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static List<File> convertFileSetToFiles(FileSet source) |
| throws IOException { |
| String includes = getCommaSeparatedList(source.getIncludes()); |
| String excludes = getCommaSeparatedList(source.getExcludes()); |
| return FileUtils.getFiles(new File(source.getDirectory()), includes, |
| excludes); |
| } |
| |
| @Override |
| public void execute() throws MojoExecutionException { |
| try { |
| SCM scm = determineScm(); |
| project.getProperties().setProperty(buildTimeProperty, getBuildTime()); |
| project.getProperties().setProperty(scmUriProperty, getSCMUri(scm)); |
| project.getProperties().setProperty(scmBranchProperty, getSCMBranch(scm)); |
| project.getProperties().setProperty(scmCommitProperty, getSCMCommit(scm)); |
| project.getProperties().setProperty(md5Property, computeMD5()); |
| } catch (Throwable ex) { |
| throw new MojoExecutionException(ex.toString(), ex); |
| } |
| } |
| |
| private String getBuildTime() { |
| DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); |
| dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| return dateFormat.format(new Date()); |
| } |
| |
| /** |
| * Determines which SCM is in use (Subversion, git, or none) and captures |
| * output of the SCM command for later parsing. |
| * |
| * @return SCM in use for this build |
| * @throws Exception if any error occurs attempting to determine SCM |
| */ |
| private SCM determineScm() throws Exception { |
| CommandExec exec = new CommandExec(this); |
| SCM scm = SCM.NONE; |
| scmOut = new ArrayList<String>(); |
| int ret = exec.run(Arrays.asList(svnCommand, "info"), scmOut); |
| if (ret == 0) { |
| scm = SCM.SVN; |
| } else { |
| ret = exec.run(Arrays.asList(gitCommand, "branch"), scmOut); |
| if (ret == 0) { |
| ret = exec.run(Arrays.asList(gitCommand, "remote", "-v"), scmOut); |
| if (ret != 0) { |
| scm = SCM.NONE; |
| scmOut = null; |
| } else { |
| ret = exec.run(Arrays.asList(gitCommand, "log", "-n", "1"), scmOut); |
| if (ret != 0) { |
| scm = SCM.NONE; |
| scmOut = null; |
| } else { |
| scm = SCM.GIT; |
| } |
| } |
| } |
| } |
| if (scmOut != null) { |
| getLog().debug(scmOut.toString()); |
| } |
| getLog().info("SCM: " + scm); |
| return scm; |
| } |
| |
| private String[] getSvnUriInfo(String str) { |
| String[] res = new String[]{ "Unknown", "Unknown" }; |
| try { |
| String path = str; |
| int index = path.indexOf("trunk"); |
| if (index > -1) { |
| res[0] = path.substring(0, index - 1); |
| res[1] = "trunk"; |
| } else { |
| index = path.indexOf("branches"); |
| if (index > -1) { |
| res[0] = path.substring(0, index - 1); |
| int branchIndex = index + "branches".length() + 1; |
| index = path.indexOf("/", branchIndex); |
| if (index > -1) { |
| res[1] = path.substring(branchIndex, index); |
| } else { |
| res[1] = path.substring(branchIndex); |
| } |
| } |
| } |
| } catch (Exception ex) { |
| getLog().warn("Could not determine URI & branch from SVN URI: " + str); |
| } |
| return res; |
| } |
| |
| @SuppressWarnings("checkstyle:AbbreviationAsWordInName") |
| private String getSCMUri(SCM scm) { |
| String uri = "Unknown"; |
| switch (scm) { |
| case SVN: |
| for (String s : scmOut) { |
| if (s.startsWith("URL:")) { |
| uri = s.substring(4).trim(); |
| uri = getSvnUriInfo(uri)[0]; |
| break; |
| } |
| } |
| break; |
| case GIT: |
| for (String s : scmOut) { |
| if (s.startsWith("origin") && s.endsWith("(fetch)")) { |
| uri = s.substring("origin".length()); |
| uri = uri.substring(0, uri.length() - "(fetch)".length()); |
| break; |
| } |
| } |
| break; |
| case NONE: |
| break; |
| default: |
| throw new IllegalArgumentException(String.format("SCM %s is not supported", |
| scm)); |
| } |
| return uri.trim(); |
| } |
| |
| @SuppressWarnings("checkstyle:AbbreviationAsWordInName") |
| private String getSCMCommit(SCM scm) { |
| String commit = "Unknown"; |
| switch (scm) { |
| case SVN: |
| for (String s : scmOut) { |
| if (s.startsWith("Revision:")) { |
| commit = s.substring("Revision:".length()); |
| break; |
| } |
| } |
| break; |
| case GIT: |
| for (String s : scmOut) { |
| if (s.startsWith("commit")) { |
| commit = s.substring("commit".length()); |
| break; |
| } |
| } |
| break; |
| case NONE: |
| break; |
| default: |
| throw new IllegalArgumentException(String.format("SCM %s is not supported", |
| scm)); |
| } |
| return commit.trim(); |
| } |
| |
| @SuppressWarnings("checkstyle:AbbreviationAsWordInName") |
| private String getSCMBranch(SCM scm) { |
| String branch = "Unknown"; |
| switch (scm) { |
| case SVN: |
| for (String s : scmOut) { |
| if (s.startsWith("URL:")) { |
| branch = s.substring(4).trim(); |
| branch = getSvnUriInfo(branch)[1]; |
| break; |
| } |
| } |
| break; |
| case GIT: |
| for (String s : scmOut) { |
| if (s.startsWith("*")) { |
| branch = s.substring("*".length()); |
| break; |
| } |
| } |
| break; |
| case NONE: |
| break; |
| default: |
| throw new IllegalArgumentException(String.format("SCM %s is not supported", |
| scm)); |
| } |
| return branch.trim(); |
| } |
| |
| private byte[] readFile(File file) throws IOException { |
| RandomAccessFile raf = new RandomAccessFile(file, "r"); |
| byte[] buffer = new byte[(int) raf.length()]; |
| raf.readFully(buffer); |
| raf.close(); |
| return buffer; |
| } |
| |
| private byte[] computeMD5(List<File> files) throws IOException, |
| NoSuchAlgorithmException { |
| MessageDigest md5 = MessageDigest.getInstance("MD5"); |
| for (File file : files) { |
| getLog().debug("Computing MD5 for: " + file); |
| md5.update(readFile(file)); |
| } |
| return md5.digest(); |
| } |
| |
| private String computeMD5() throws Exception { |
| List<File> files = convertFileSetToFiles(source); |
| // File order of MD5 calculation is significant. Sorting is done on |
| // unix-format names, case-folded, in order to get a platform-independent |
| // sort and calculate the same MD5 on all platforms. |
| Collections.sort(files, new Comparator<File>() { |
| @Override |
| public int compare(File lhs, File rhs) { |
| return normalizePath(lhs).compareTo(normalizePath(rhs)); |
| } |
| |
| private String normalizePath(File file) { |
| return file.getPath().toUpperCase().replaceAll("\\\\", "/"); |
| } |
| }); |
| byte[] md5 = computeMD5(files); |
| String md5str = byteArrayToString(md5); |
| getLog().info("Computed MD5: " + md5str); |
| return md5str; |
| } |
| |
| private String byteArrayToString(byte[] array) { |
| StringBuilder sb = new StringBuilder(); |
| for (byte b : array) { |
| sb.append(Integer.toHexString(0xff & b)); |
| } |
| return sb.toString(); |
| } |
| |
| @SuppressWarnings("checkstyle:AbbreviationAsWordInName") |
| private enum SCM { |
| NONE, SVN, GIT |
| } |
| } |