Java tutorial
/** * Copyright 2012-2014 Julien Eluard and contributors * This project includes software developed by Julien Eluard: https://github.com/jeluard/ * * 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.semver.enforcer; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.enforcer.rule.api.EnforcerRule; import org.apache.maven.enforcer.rule.api.EnforcerRuleException; import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.osjava.jardiff.DiffCriteria; import org.osjava.jardiff.PublicDiffCriteria; import org.osjava.jardiff.SimpleDiffCriteria; import org.semver.Comparer; import org.semver.Delta; import org.semver.Dumper; import org.semver.Version; /** * Abstract {@link EnforcerRule} implementation providing facilities for compatibility checking. */ public abstract class AbstractEnforcerRule implements EnforcerRule { private static final String JAR_ARTIFACT_TYPE = "jar"; private static final String BUNDLE_ARTIFACT_TYPE = "bundle"; /** * Version number of artifact to be checked. * * @parameter */ private String previousVersion; /** * Class names to be included. * * @parameter */ private String[] includes; /** * Class names to be excluded. * * @parameter */ private String[] excludes; /** * Dump change details. * * @parameter */ private boolean dumpDetails = false; /** * Check public members only * * @parameter */ private boolean publicOnly = false; private Set<String> extractFilters(final String[] filtersAsStringArray) { if (filtersAsStringArray == null) { return Collections.emptySet(); } return new HashSet<String>(Arrays.asList(filtersAsStringArray)); } @Override public void execute(final EnforcerRuleHelper helper) throws EnforcerRuleException { final MavenProject project = getMavenProject(helper); if (shouldSkipRuleExecution(project)) { helper.getLog().debug("Skipping non " + AbstractEnforcerRule.JAR_ARTIFACT_TYPE + " or " + BUNDLE_ARTIFACT_TYPE + " artifact."); return; } final Artifact previousArtifact; final Artifact currentArtifact = validateArtifact(project.getArtifact()); final Version current = Version.parse(currentArtifact.getVersion()); try { final ArtifactRepository localRepository = (ArtifactRepository) helper.evaluate("${localRepository}"); final String version; if (this.previousVersion != null) { version = this.previousVersion; helper.getLog().info("Version specified as <" + version + ">"); } else { final ArtifactMetadataSource artifactMetadataSource = (ArtifactMetadataSource) helper .getComponent(ArtifactMetadataSource.class); final List<ArtifactVersion> availableVersions = getAvailableReleasedVersions(artifactMetadataSource, project, localRepository); final List<ArtifactVersion> availablePreviousVersions = filterNonPreviousVersions(availableVersions, current); if (availablePreviousVersions.isEmpty()) { helper.getLog() .warn("No previously released version. Backward compatibility check not performed."); return; } version = availablePreviousVersions.iterator().next().toString(); helper.getLog().info("Version deduced as <" + version + "> (among all availables: " + availablePreviousVersions + ")"); } final ArtifactFactory artifactFactory = (ArtifactFactory) helper.getComponent(ArtifactFactory.class); previousArtifact = artifactFactory.createArtifact(project.getGroupId(), project.getArtifactId(), version, null, project.getArtifact().getType()); final ArtifactResolver resolver = (ArtifactResolver) helper.getComponent(ArtifactResolver.class); resolver.resolve(previousArtifact, project.getRemoteArtifactRepositories(), localRepository); validateArtifact(previousArtifact); } catch (Exception e) { helper.getLog().warn("Exception while accessing artifacts; skipping check.", e); return; } final Version previous = Version.parse(previousArtifact.getVersion()); final File previousJar = previousArtifact.getFile(); final File currentJar = currentArtifact.getFile(); compareJars(helper, previous, previousJar, current, currentJar); } protected abstract void enforce(final EnforcerRuleHelper helper, final Delta delta, final Version previous, final Version current) throws EnforcerRuleException; protected final void fail(final Delta delta, final String message) throws EnforcerRuleException { if (this.dumpDetails) { Dumper.dump(delta); } throw new EnforcerRuleException(message); } /** * @param artifactMetadataSource * @param project * @param localRepository * @return all available versions from most recent to oldest * @throws ArtifactMetadataRetrievalException */ protected final List<ArtifactVersion> getAvailableReleasedVersions( final ArtifactMetadataSource artifactMetadataSource, final MavenProject project, final ArtifactRepository localRepository) throws ArtifactMetadataRetrievalException { final List<ArtifactVersion> availableVersions = artifactMetadataSource.retrieveAvailableVersions( project.getArtifact(), localRepository, project.getRemoteArtifactRepositories()); availableVersions.remove(new DefaultArtifactVersion(project.getArtifact().getVersion())); for (final Iterator<ArtifactVersion> iterator = availableVersions.iterator(); iterator.hasNext();) { final ArtifactVersion artifactVersion = iterator.next(); if (Version.parse(artifactVersion.toString()).isSnapshot()) { iterator.remove(); } } //TODO proper sorting based on Version Collections.sort(availableVersions); Collections.reverse(availableVersions); return availableVersions; } protected final List<ArtifactVersion> filterNonPreviousVersions(final List<ArtifactVersion> availableVersions, final Version version) { final List<ArtifactVersion> versions = new ArrayList<ArtifactVersion>(); for (final ArtifactVersion artifactVersion : availableVersions) { Version parsedVersion = Version.parse(artifactVersion.toString()); if (version.isCompatible(parsedVersion) && version.compareTo(parsedVersion) > 0) { versions.add(artifactVersion); } } return versions; } private static MavenProject getMavenProject(EnforcerRuleHelper helper) throws EnforcerRuleException { final MavenProject project; try { project = (MavenProject) helper.evaluate("${project}"); } catch (ExpressionEvaluationException e) { throw new EnforcerRuleException("Failed to access ${project} variable", e); } return project; } private static boolean shouldSkipRuleExecution(MavenProject project) { return !AbstractEnforcerRule.JAR_ARTIFACT_TYPE.equals(project.getArtifact().getType()) && !AbstractEnforcerRule.BUNDLE_ARTIFACT_TYPE.equals(project.getArtifact().getType()); } private void compareJars(final EnforcerRuleHelper helper, final Version previous, final File previousJar, final Version current, final File currentJar) throws EnforcerRuleException { helper.getLog().info("Using <" + previousJar + "> as previous JAR"); helper.getLog().info("Using <" + currentJar + "> as current JAR"); try { final DiffCriteria diffCriteria = publicOnly ? new PublicDiffCriteria() : new SimpleDiffCriteria(); final Comparer comparer = new Comparer(diffCriteria, previousJar, currentJar, extractFilters(this.includes), extractFilters(this.excludes)); final Delta delta = comparer.diff(); enforce(helper, delta, previous, current); } catch (IOException e) { throw new EnforcerRuleException("Exception while checking compatibility: " + e.toString(), e); } } /** * Validates that specified {@link Artifact} is a file. * @param artifact */ private static Artifact validateArtifact(final Artifact artifact) { if (!artifact.getFile().isFile()) { throw new IllegalArgumentException("<" + artifact.getFile() + "> is not a file"); } return artifact; } @Override public boolean isCacheable() { return false; } @Override public boolean isResultValid(final EnforcerRule cachedRule) { return false; } @Override public String getCacheId() { return "0"; } }