Java tutorial
/* * Copyright 2000-2014 Vaadin Ltd. * * 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.vaadin.integration.maven.wscdn; import com.fasterxml.jackson.databind.ObjectMapper; import static java.lang.Integer.parseInt; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.text.MessageFormat; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; import org.apache.commons.io.IOUtils; //import elemental.json.JsonException; //import elemental.json.JsonNull; //import elemental.json.JsonObject; //import elemental.json.impl.JsonUtil; /** * This class is able to validate the vaadin CVAL license. * * It reads the developer license file and asks the server to validate the * licenseKey. If the license is invalid it throws an exception with the * information about the problem and the server response. * * @since 7.3 */ public final class CvalChecker { /* * The class with the method for getting json from server side. It is here * and protected just for replacing it in tests. */ public static class CvalServer { protected String licenseUrl = LICENSE_URL_PROD; String askServer(String productName, String productKey, int timeoutMs) throws IOException { String url = licenseUrl + productKey; URLConnection con; try { // Send some additional info in the User-Agent string. String ua = "Cval " + productName + " " + productKey + " " + getFirstLaunch(); for (String prop : Arrays.asList("java.vendor.url", "java.version", "os.name", "os.version", "os.arch")) { ua += " " + System.getProperty(prop, "-").replace(" ", "_"); } con = new URL(url).openConnection(); con.setRequestProperty("User-Agent", ua); con.setConnectTimeout(timeoutMs); con.setReadTimeout(timeoutMs); String r = IOUtils.toString(con.getInputStream()); return r; } catch (MalformedURLException e) { e.printStackTrace(); return null; } } /* * Get the GWT firstLaunch timestamp. */ String getFirstLaunch() { try { Class<?> clz = Class.forName("com.google.gwt.dev.shell.CheckForUpdates"); return Preferences.userNodeForPackage(clz).get("firstLaunch", "-"); } catch (ClassNotFoundException e) { return "-"; } } } /** * Exception thrown when the user does not have a valid cval license. */ public static class InvalidCvalException extends Exception { private static final long serialVersionUID = 1L; public final CvalInfo info; public final String name; public final String key; public final String version; public final String title; public InvalidCvalException(String name, String version, String title, String key, CvalInfo info) { super(composeMessage(title, version, key, info)); this.info = info; this.name = name; this.key = key; this.version = version; this.title = title; } static String composeMessage(String title, String version, String key, CvalInfo info) { String msg = ""; int majorVers = computeMajorVersion(version); if (info != null && !info.isValidVersion(majorVers)) { msg = getErrorMessage("invalid", title, majorVers); } else if (info != null && info.getMessage() != null) { msg = info.getMessage().replace("\\n", "\n"); } else if (info != null && info.isLicenseExpired()) { String type = "evaluation".equals(info.getType()) ? "Evaluation license" : "License"; msg = getErrorMessage("expired", title, majorVers, type); } else if (key == null) { msg = getErrorMessage("none", title, majorVers); } else { msg = getErrorMessage("invalid", title, majorVers); } return msg; } } /** * Exception thrown when the license server is unreachable */ public static class UnreachableCvalServerException extends Exception { private static final long serialVersionUID = 1L; public final String name; public UnreachableCvalServerException(String name, Exception e) { super(e); this.name = name; } } public static final String LINE = "----------------------------------------------------------------------------------------------------------------------"; static final int GRACE_DAYS_MSECS = 2 * 24 * 60 * 60 * 1000; private static final String LICENSE_URL_PROD = "https://tools.vaadin.com/vaadin-license-server/licenses/"; /* * used in tests */ static void cacheLicenseInfo(CvalInfo info) { if (info != null) { Preferences p = Preferences.userNodeForPackage(CvalInfo.class); if (info.toString().length() > Preferences.MAX_VALUE_LENGTH) { // This should never happen since MAX_VALUE_LENGTH is big // enough. // But server could eventually send a very big message, so we // discard it in cache and would use hard-coded messages. info.setMessage(null); } p.put(info.getProduct().getName(), info.toString()); } } /* * used in tests */ static void deleteCache(String productName) { Preferences p = Preferences.userNodeForPackage(CvalInfo.class); p.remove(productName); } /** * Given a product name returns the name of the file with the license key. * * Traditionally we have delivered license keys with a name like * 'vaadin.touchkit.developer.license' but our database product name is * 'vaadin-touchkit' so we have to replace '-' by '.' to maintain * compatibility. */ static final String computeLicenseName(String productName) { return productName.replace("-", ".") + ".developer.license"; } static final int computeMajorVersion(String productVersion) { return productVersion == null || productVersion.isEmpty() ? 0 : parseInt(productVersion.replaceFirst("[^\\d]+.*$", "")); } /* * used in tests */ static CvalInfo parseJson(String json) { if (json == null) { return null; } ObjectMapper objectMapper = new ObjectMapper(); try { CvalInfo readValue = objectMapper.readValue(json, CvalInfo.class); return readValue; } catch (Exception e) { Logger.getLogger(CvalChecker.class.getName()).log(Level.WARNING, "Parsing json message failed: {0}", json); throw new RuntimeException(e); } } private CvalServer provider; /** * The constructor. */ public CvalChecker() { setLicenseProvider(new CvalServer()); } /** * Validate whether there is a valid license key for a product. * * @param productName for example vaadin-touchkit * @param productVersion for instance 4.0.1 * @param productTitle yes, there is no documentation for this. * @return CvalInfo Server response or cache response if server is offline * @throws InvalidCvalException when there is no a valid license for the * product * @throws UnreachableCvalServerException when we have license key but * server is unreachable */ public CvalInfo validateProduct(String productName, String productVersion, String productTitle) throws InvalidCvalException, UnreachableCvalServerException { String key = getDeveloperLicenseKey(productName, productVersion, productTitle); CvalInfo info = null; if (key != null && !key.isEmpty()) { info = getCachedLicenseInfo(productName); if (info != null && !info.isValidInfo(productName, key)) { deleteCache(productName); info = null; } info = askLicenseServer(productName, key, productVersion, info); if (info != null && info.isValidInfo(productName, key) && info.isValidVersion(computeMajorVersion(productVersion)) && !info.isLicenseExpired()) { return info; } } throw new InvalidCvalException(productName, productVersion, productTitle, key, info); } /* * Change the license provider, only used in tests. */ final CvalChecker setLicenseProvider(CvalServer p) { provider = p; return this; } private CvalInfo askLicenseServer(String productName, String productKey, String productVersion, CvalInfo info) throws UnreachableCvalServerException { int majorVersion = computeMajorVersion(productVersion); // If we have a valid license info here, it means that we got it from // cache. // We add a grace time when so as if the server is unreachable // we allow the user to use the product. if (info != null && info.getExpiredEpochDate() != null && !"evaluation".equals(info.getType())) { long ts = info.getExpiredEpochDate().getTime() + GRACE_DAYS_MSECS; info.setExpiredEpoch(new Date(ts).getTime()); } boolean validCache = info != null && info.isValidInfo(productName, productKey) && info.isValidVersion(majorVersion) && !info.isLicenseExpired(); // if we have a validCache we set the timeout smaller int timeout = validCache ? 2000 : 10000; try { final String answer = provider.askServer(productName + "-" + productVersion, productKey, timeout); CvalInfo srvinfo = parseJson(answer); if (srvinfo != null && srvinfo.isValidInfo(productName, productKey) && srvinfo.isValidVersion(majorVersion)) { // We always cache the info if it is valid although it is // expired cacheLicenseInfo(srvinfo); info = srvinfo; } } catch (FileNotFoundException e) { // 404 return null; } catch (Exception e) { if (info == null) { throw new UnreachableCvalServerException(productName, e); } } return info; } private CvalInfo getCachedLicenseInfo(String productName) { Preferences p = Preferences.userNodeForPackage(CvalInfo.class); String json = p.get(productName, ""); if (!json.isEmpty()) { try { CvalInfo info = parseJson(json); if (info != null) { return info; } } catch (RuntimeException e) { try { p.clear(); } catch (BackingStoreException ex) { Logger.getLogger(CvalChecker.class.getName()).log(Level.SEVERE, null, ex); } throw e; } } return null; } private String getDeveloperLicenseKey(String productName, String productVersion, String productTitle) throws InvalidCvalException { String licenseName = computeLicenseName(productName); String key = System.getProperty(licenseName); if (key != null && !key.isEmpty()) { return key; } try { String dotLicenseName = "." + licenseName; String userHome = System.getProperty("user.home"); for (URL url : new URL[] { new File(userHome, dotLicenseName).toURI().toURL(), new File(userHome, licenseName).toURI().toURL(), URL.class.getResource("/" + dotLicenseName), URL.class.getResource("/" + licenseName) }) { if (url != null) { try { key = readKeyFromFile(url, computeMajorVersion(productVersion)); if (key != null && !(key = key.trim()).isEmpty()) { return key; } } catch (IOException ignored) { } } } } catch (Exception e) { e.printStackTrace(); } throw new InvalidCvalException(productName, productVersion, productTitle, null, null); } String readKeyFromFile(URL url, int majorVersion) throws IOException { String majorVersionStr = String.valueOf(majorVersion); List<String> lines = IOUtils.readLines(url.openStream()); String defaultKey = null; for (String line : lines) { String[] parts = line.split("\\s*=\\s*"); if (parts.length < 2) { defaultKey = parts[0].trim(); } if (parts[0].equals(majorVersionStr)) { return parts[1].trim(); } } return defaultKey; } static String getErrorMessage(String key, Object... pars) { Locale loc = Locale.getDefault(); ResourceBundle res = ResourceBundle.getBundle(CvalChecker.class.getName(), loc); String msg = res.getString(key); return new MessageFormat(msg, loc).format(pars); } }