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

com.arcbees.chosen.rebind.VersionInspectorLinker Maven / Gradle / Ivy

/*
 * Copyright 2015 ArcBees Inc.
 *
 * 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 com.arcbees.chosen.rebind;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.gwt.core.ext.Linker;
import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.ConfigurationProperty;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.Shardable;

import static com.google.gwt.core.ext.TreeLogger.Type.DEBUG;

@Shardable
@LinkerOrder(Order.POST)
public class VersionInspectorLinker extends Linker {
    private static final int CHECK_TIMEOUT_MS = 5000;

    private static final String CHOSEN_EXIST_CLASS = "com.arcbees.chosen.client.Chosen";
    private static final String CHOSEN_GROUP_ID = "com.arcbees";
    private static final String CHOSEN_ARTIFACT = "gwtchosen";
    private static final Dependency CHOSEN_DEPENDENCY =
            new DependencyImpl(CHOSEN_EXIST_CLASS, CHOSEN_GROUP_ID, CHOSEN_ARTIFACT);

    private static final String API_SEARCH = "http://arcbees-stats-service.appspot.com/version";
    private static final String GROUP_QUERY_PARAMETER = "groupid";
    private static final String ARTIFACT_QUERY_PARAMETER = "artifactid";
    private static final String VERSION_QUERY_PARAMETER = "version";

    private static final String PROPERTY_VERIFY_NEWER_VERSION = "verifyNewerVersion";

    private static final Pattern VERSION_PATTERN = Pattern.compile("\"version\":\\s*\"([0-9](?:\\.[0-9])*)\"");
    private static final Pattern LATEST_PATTERN = Pattern.compile("\"latest\":([a-z]{1,4})");
    private static final Pattern RESPONSE_CONTENT_PATTERN = Pattern.compile("^[^{]*(\\{.*\\})$");

    private static final String HR = "------------------------------------------------------------";
    private static final String NEW_VERSION_AVAILABLE = "A new version of %s is available!";
    private static final String SEE_ARTIFACT_DETAILS = "See http://search.maven.org/#artifactdetails|%s|%s|%s|jar";
    private static final String YOUR_VERSION = "Your version: %s";
    private static final String LATEST_VERSION = "Latest version: %s";

    private Logger logger;

    public VersionInspectorLinker() {
    }

    @Override
    public String getDescription() {
        return "Verify the availability of a more recent version of GWTChosen.";
    }

    @Override
    public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts, boolean onePermutation)
            throws UnableToCompleteException {
        if (!onePermutation && !isSuperDevMode(context) && !isTest(context) && canVerifyNewerVersion(context)) {
            checkLatestVersions(logger);
        }

        return artifacts;
    }

    private boolean isSuperDevMode(LinkerContext context) {
        boolean isSdm = false;

        for (SelectionProperty property : context.getProperties()) {
            if ("superdevmode".equals(property.getName())) {
                isSdm = "on".equals(property.tryGetValue());
                break;
            }
        }

        return isSdm;
    }

    private boolean isTest(LinkerContext context) {
        boolean isTest = false;

        for (ConfigurationProperty property : context.getConfigurationProperties()) {
            String name = property.getName();
            List values = property.getValues();

            if ("junit.moduleName".equals(name) && !values.isEmpty() && !values.get(0).isEmpty()) {
                isTest = true;
                break;
            }
        }

        return isTest;
    }

    private boolean canVerifyNewerVersion(LinkerContext context) {
        boolean verifyNewerVersion = true;

        for (ConfigurationProperty property : context.getConfigurationProperties()) {
            if (PROPERTY_VERIFY_NEWER_VERSION.equals(property.getName())) {
                List values = property.getValues();
                verifyNewerVersion = !(!values.isEmpty() && "false".equals(values.get(0)));

                break;
            }
        }

        return verifyNewerVersion;
    }

    private void checkLatestVersions(TreeLogger logger) {
        checkLatestVersionIfPresent(logger, CHOSEN_DEPENDENCY);
    }

    private void checkLatestVersionIfPresent(TreeLogger baseLogger, Dependency dependency) {
        if (dependency.isPresent()) {
            String artifactId = dependency.getArtifactId();
            logger = new Logger(baseLogger.branch(DEBUG, "Checking version information for " + artifactId));
            logger.debug("You can disable this check by adding this line to your GWT module:");
            logger.debug("");

            checkLatestVersion(dependency);
        }
    }

    private void checkLatestVersion(Dependency dependency) {
        try {
            String groupId = dependency.getGroupId();
            String artifactId = dependency.getArtifactId();
            String currentVersion = dependency.getVersion();

            String json = fetchArtifactVersion(groupId, artifactId, currentVersion);
            String latestVersion = extractVersion(json);
            Boolean isLatest = extractIsLatest(json);

            if (isLatest) {
                logger.info("You are using the latest version of " + artifactId + "!");
            } else {
                warnVersion(groupId, artifactId, latestVersion, currentVersion);
            }
        } catch (Exception e) {
            logger.getTreeLogger().log(DEBUG, "Exception caught", e);
        }
    }

    /**
     * Calls the Arcbees Stats service to retrieve data about a GWTP artifact. Note: We would use URL.openConnection,
     * but because this code may be used in a GAE environment with hosted mode, this will cause fallback to the URLFetch
     * Service. Using a socket bypasses this.
     *
     * @return The resulting plain text response
     */
    private String fetchArtifactVersion(String groupId, String artifactId, String version) throws IOException {
        URL maven = new URL(buildUrl(groupId, artifactId, version));

        Socket socket = new Socket(maven.getHost(), 80);
        socket.setSoTimeout(CHECK_TIMEOUT_MS);

        String response;

        try {
            PrintWriter output = new PrintWriter(new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream(), "UTF-8")));
            output.println("GET " + maven.getFile() + " HTTP/1.1");
            output.println("Host: " + maven.getHost());
            output.println("Connection: close");
            output.println();
            output.flush();

            response = readText(socket.getInputStream());
        } finally {
            socket.close();
        }

        return response;
    }

    private String readText(InputStream stream) throws IOException {
        BufferedReader input = new BufferedReader(new InputStreamReader(stream, "UTF-8"));

        StringBuilder response = new StringBuilder();
        String inputLine;
        while ((inputLine = input.readLine()) != null) {
            response.append(inputLine);
        }

        return extractResponseContent(response.toString());
    }

    private String extractResponseContent(String response) {
        Matcher matcher = RESPONSE_CONTENT_PATTERN.matcher(response);

        return matcher.find() ? matcher.group(1) : "";
    }

    private String extractVersion(String versionResponse) {
        Matcher matcher = VERSION_PATTERN.matcher(versionResponse);

        return matcher.find() ? matcher.group(1) : "";
    }

    private Boolean extractIsLatest(String versionResponse) {
        Matcher matcher = LATEST_PATTERN.matcher(versionResponse);

        return matcher.find() && Boolean.valueOf(matcher.group(1));
    }

    private void warnVersion(String groupId, String artifactId, String latestVersion, String currentVersion) {
        logger.warn(HR);

        logger.warn(NEW_VERSION_AVAILABLE, artifactId);
        logger.warn(YOUR_VERSION, currentVersion);
        logger.warn(LATEST_VERSION, latestVersion);
        logger.warn(SEE_ARTIFACT_DETAILS, groupId, artifactId, latestVersion);

        logger.warn(HR);
    }

    private String buildUrl(String groupId, String artifactId, String version) {
        StringBuilder queryString = new StringBuilder();

        appendParameter(queryString, GROUP_QUERY_PARAMETER, groupId);
        appendParameter(queryString, ARTIFACT_QUERY_PARAMETER, artifactId);
        appendParameter(queryString, VERSION_QUERY_PARAMETER, version);

        queryString.insert(0, API_SEARCH);

        return queryString.toString();
    }

    private void appendParameter(StringBuilder stringBuilder, String key, String value) {
        if (stringBuilder.length() == 0) {
            stringBuilder.append("?").append(key).append("=").append(value);
        } else {
            stringBuilder.append("&").append(key).append("=").append(value);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy