001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.enforcer.rules.version;
020
021import javax.inject.Named;
022
023import java.util.Arrays;
024import java.util.Iterator;
025import java.util.List;
026import java.util.regex.Matcher;
027import java.util.regex.Pattern;
028
029import org.apache.commons.lang3.SystemUtils;
030import org.apache.maven.artifact.versioning.ArtifactVersion;
031import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
032import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
033import org.apache.maven.artifact.versioning.VersionRange;
034import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
035import org.codehaus.plexus.util.StringUtils;
036
037/**
038 * This rule checks that the Java version is allowed.
039 *
040 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
041 */
042@Named("requireJavaVersion")
043public final class RequireJavaVersion extends AbstractVersionEnforcer {
044
045    private static final Pattern JDK8_VERSION_PATTERN = Pattern.compile("([\\d.]+)");
046
047    /**
048     * Display the normalized JDK version.
049     */
050    private boolean display = false;
051
052    @Override
053    public void setVersion(String theVersion) {
054
055        if ("8".equals(theVersion)) {
056            super.setVersion("1.8");
057            return;
058        }
059
060        if (!theVersion.contains("8")) {
061            super.setVersion(theVersion);
062            return;
063        }
064
065        Matcher matcher = JDK8_VERSION_PATTERN.matcher(theVersion);
066
067        StringBuffer result = new StringBuffer();
068        while (matcher.find()) {
069            if ("8".equals(matcher.group(1))) {
070                matcher.appendReplacement(result, "1.8");
071            } else {
072                matcher.appendReplacement(result, "$1");
073            }
074        }
075        matcher.appendTail(result);
076
077        super.setVersion(result.toString());
078    }
079
080    @Override
081    public void execute() throws EnforcerRuleException {
082        String javaVersion = SystemUtils.JAVA_VERSION;
083        String javaVersionNormalized = normalizeJDKVersion(javaVersion);
084        if (display) {
085            getLog().info("Detected Java Version: '" + javaVersion + "'");
086            getLog().info("Normalized Java Version: '" + javaVersionNormalized + "'");
087        } else {
088            getLog().debug("Detected Java Version: '" + javaVersion + "'");
089            getLog().debug("Normalized Java Version: '" + javaVersionNormalized + "'");
090        }
091
092        ArtifactVersion detectedJdkVersion = new DefaultArtifactVersion(javaVersionNormalized);
093
094        getLog().debug("Parsed Version: Major: " + detectedJdkVersion.getMajorVersion() + " Minor: "
095                + detectedJdkVersion.getMinorVersion() + " Incremental: " + detectedJdkVersion.getIncrementalVersion()
096                + " Build: " + detectedJdkVersion.getBuildNumber() + " Qualifier: "
097                + detectedJdkVersion.getQualifier());
098
099        setCustomMessageIfNoneConfigured(detectedJdkVersion, getVersion());
100
101        enforceVersion("JDK", getVersion(), detectedJdkVersion);
102    }
103
104    /**
105     * Converts a jdk string from 1.5.0-11b12 to a single 3 digit version like 1.5.0-11
106     *
107     * @param theJdkVersion to be converted.
108     * @return the converted string.
109     */
110    public static String normalizeJDKVersion(String theJdkVersion) {
111
112        theJdkVersion = theJdkVersion.replaceAll("_|-", ".");
113        String tokenArray[] = StringUtils.split(theJdkVersion, ".");
114        List<String> tokens = Arrays.asList(tokenArray);
115        StringBuilder buffer = new StringBuilder(theJdkVersion.length());
116
117        Iterator<String> iter = tokens.iterator();
118        for (int i = 0; i < tokens.size() && i < 4; i++) {
119            String section = iter.next();
120            section = section.replaceAll("[^0-9]", "");
121
122            if (section != null && !section.isEmpty()) {
123                buffer.append(Integer.parseInt(section));
124
125                if (i != 2) {
126                    buffer.append('.');
127                } else {
128                    buffer.append('-');
129                }
130            }
131        }
132
133        String version = buffer.toString();
134        version = StringUtils.stripEnd(version, "-");
135        return StringUtils.stripEnd(version, ".");
136    }
137
138    private void setCustomMessageIfNoneConfigured(ArtifactVersion detectedJdkVersion, String allowedVersionRange) {
139        if (getMessage() == null) {
140            String version;
141            try {
142                VersionRange vr = VersionRange.createFromVersionSpec(allowedVersionRange);
143                version = AbstractVersionEnforcer.toString(vr);
144            } catch (InvalidVersionSpecificationException e) {
145                getLog().debug("Could not parse allowed version range " + allowedVersionRange + " " + e.getMessage());
146                version = allowedVersionRange;
147            }
148            String message = String.format(
149                    "Detected JDK version %s (JAVA_HOME=%s) is not in the allowed range %s.",
150                    detectedJdkVersion, SystemUtils.JAVA_HOME, version);
151            super.setMessage(message);
152        }
153    }
154
155    @Override
156    public String toString() {
157        return String.format(
158                "%s[message=%s, version=%s, display=%b]",
159                getClass().getSimpleName(), getMessage(), getVersion(), display);
160    }
161}