Browse Source

License Checker for vaadin cval products (#13696 #13474)

- This patch includes four elements:
  1.- A class able to validate a licensed product against Vaadin
      license server. It can be used in any vaadin product (thought
      for non addons like TB) just adding vaadin dependency, or
      copying the class.
  2.- A class able to inspect all addons in the classpath and figure
      out, based on the MANIFEST.MF info, whether we have to check
      developer license.
  3.- A modification to Vaadin connector generator to use the classes
      above and to stop compilation in case.
  4.- A modification to ConnectorBundleLoader, so as when a new connector
      is instatiated, we check whether it is using an evaluation
      license and show a notice. We only show the notice once.

- In addition to validating developer licenses, the checker caches the
  server response for using it when there are connection problems.
- This stuff is in Vaadin core, so as we dont maintain license code in
  each addon. For checking an addon license we just add the license type
  to the manifest when packaging the artefact.
- It checks expiration time, product name and major version.

Fixes: #13696
In some-way related with: #13474

Change-Id: Ib61b1c2e9c3cacd463a1ce5db02c2cfbc06851c2
tags/7.3.0.beta1
Manolo Carrasco 10 years ago
parent
commit
9a84fb14f2

+ 1
- 0
.classpath View File

@@ -6,6 +6,7 @@
<classpathentry kind="src" path="client/src"/>
<classpathentry kind="src" path="server/src"/>
<classpathentry kind="src" path="client-compiler/src"/>
<classpathentry kind="src" path="client-compiler/tests/src"/>
<classpathentry kind="src" path="uitest/src"/>
<classpathentry kind="src" path="buildhelpers/src"/>
<classpathentry kind="src" path="shared/src"/>

+ 39
- 5
client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java View File

@@ -1,12 +1,12 @@
/*
* 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
@@ -48,6 +48,7 @@ import com.vaadin.client.JsArrayObject;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo;
import com.vaadin.client.metadata.InvokationHandler;
import com.vaadin.client.metadata.OnStateChangeMethod;
import com.vaadin.client.metadata.ProxyHandler;
@@ -70,6 +71,9 @@ import com.vaadin.shared.communication.ClientRpc;
import com.vaadin.shared.communication.ServerRpc;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.Connect.LoadStyle;
import com.vaadin.tools.CvalAddonsChecker;
import com.vaadin.tools.CvalChecker;
import com.vaadin.tools.CvalChecker.InvalidCvalException;

public class ConnectorBundleLoaderFactory extends Generator {
/**
@@ -211,6 +215,8 @@ public class ConnectorBundleLoaderFactory extends Generator {

}

private CvalAddonsChecker cvalChecker = new CvalAddonsChecker();

@Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
@@ -231,7 +237,6 @@ public class ConnectorBundleLoaderFactory extends Generator {
logger.log(Type.ERROR, getClass() + " failed", e);
throw new UnableToCompleteException();
}

}

private void generateClass(TreeLogger logger, GeneratorContext context,
@@ -243,6 +248,23 @@ public class ConnectorBundleLoaderFactory extends Generator {
return;
}

List<CValUiInfo> cvalInfos = null;
try {
if (cvalChecker != null) {
cvalInfos = cvalChecker.run();
// Don't run twice
cvalChecker = null;
}
} catch (InvalidCvalException e) {
System.err.println("\n\n\n\n" + CvalChecker.LINE);
for (String line : e.getMessage().split("\n")) {
System.err.println(line);
}
System.err.println(CvalChecker.LINE + "\n\n\n\n");
System.exit(1);
throw new UnableToCompleteException();
}

List<ConnectorBundle> bundles = buildBundles(logger,
context.getTypeOracle());

@@ -364,6 +386,18 @@ public class ConnectorBundleLoaderFactory extends Generator {
w.println("});");
}

if (cvalInfos != null && !cvalInfos.isEmpty()) {
w.println("{");
for (CValUiInfo c : cvalInfos) {
if ("evaluation".equals(c.type)) {
w.println("cvals.add(new CValUiInfo(\"" + c.product
+ "\", \"" + c.version + "\", \"" + c.widgetset
+ "\", null));");
}
}
w.println("}");
}

w.outdent();
w.println("}");

@@ -1101,7 +1135,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
* {@link ServerConnector} that have a @{@link Connect} annotation. It also
* checks that multiple connectors aren't connected to the same server-side
* class.
*
*
* @param logger
* the logger to which information can be logged
* @param typeOracle

+ 191
- 0
client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java View File

@@ -0,0 +1,191 @@
/*
* 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.tools;

import static com.vaadin.tools.CvalChecker.LINE;
import static com.vaadin.tools.CvalChecker.computeMajorVersion;
import static com.vaadin.tools.CvalChecker.getErrorMessage;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo;
import com.vaadin.tools.CvalChecker.CvalInfo;
import com.vaadin.tools.CvalChecker.CvalServer;
import com.vaadin.tools.CvalChecker.InvalidCvalException;
import com.vaadin.tools.CvalChecker.UnreachableCvalServerException;

/**
* This class is able to visit all MANIFEST.MF files present in the classpath,
* filter by name, and check if the user has a valid license.
*
* Manifest files should have a few attributes indicating the license type of
* the addon:
* <ul>
* <li>Implementation-Version: 4.x.x
* <li>AdVaaName: addon_name
* <li>AdVaaLicen: cval, agpl, empty
* <li>AdVaaPkg: package of the widgets in this addon
* </ul>
*
* The class also have a method to check just one product.
*/
public final class CvalAddonsChecker {

// Manifest attributes
public static final String VAADIN_ADDON_LICENSE = "AdVaaLicen";
public static final String VAADIN_ADDON_NAME = "AdVaaName";
public static final String VAADIN_ADDON_WIDGETSET = "Vaadin-Widgetsets";
public static final String VAADIN_ADDON_VERSION = "Implementation-Version";
public static final String VAADIN_ADDON_TITLE = "Implementation-Title";

// License types
public static final String VAADIN_AGPL = "agpl";
public static final String VAADIN_CVAL = "cval";

private CvalChecker cvalChecker = new CvalChecker();
private String filterPattern;

/**
* The constructor.
*/
public CvalAddonsChecker() {
setLicenseProvider(new CvalServer());
setFilter(".*vaadin.*");
}

/**
* Visit all MANIFEST.MF files in the classpath validating licenses.
*
* Return a list of Cval licensed products in order to have enough info to
* generate nag messages in the UI.
*/
public List<CValUiInfo> run() throws InvalidCvalException {
List<CValUiInfo> ret = new ArrayList<CValUiInfo>();
try {
// Visit all MANIFEST in our classpath
Enumeration<URL> manifests = Thread.currentThread()
.getContextClassLoader()
.getResources(JarFile.MANIFEST_NAME);
while (manifests.hasMoreElements()) {
try {
URL url = manifests.nextElement();
// Discard manifests whose name does not match the filter
// pattern
if (!url.getPath().matches(filterPattern)) {
continue;
}
InputStream is = url.openStream();
// Should never happen, but we don't want a NPE here
if (is == null) {
continue;
}
// Read manifest attributes
Manifest manifest = new Manifest(is);
Attributes attribs = manifest.getMainAttributes();
String license = attribs.getValue(VAADIN_ADDON_LICENSE);
String name = attribs.getValue(VAADIN_ADDON_NAME);
String vers = attribs.getValue(VAADIN_ADDON_VERSION) == null ? ""
: attribs.getValue(VAADIN_ADDON_VERSION);
String title = attribs.getValue(VAADIN_ADDON_TITLE) == null ? name
: attribs.getValue(VAADIN_ADDON_TITLE);

String widgetsets = attribs
.getValue(VAADIN_ADDON_WIDGETSET) == null ? name
: attribs.getValue(VAADIN_ADDON_WIDGETSET);

if (name == null || license == null) {
continue;
}
if (VAADIN_AGPL.equals(license)) {
// For agpl version we print an info message
printAgplLicense(title, vers);
} else if (VAADIN_CVAL.equals(license)) {
// We only check cval licensed products
CvalInfo info;
try {
info = cvalChecker.validateProduct(name, vers,
title);
printValidLicense(info, title, vers);
} catch (UnreachableCvalServerException e) {
info = CvalChecker.parseJson("{'product':{'name':'"
+ name + "'}}");
printServerUnreachable(title, vers);
}
for (String w : widgetsets.split("[, ]+")) {
ret.add(new CValUiInfo(title, String
.valueOf(computeMajorVersion(vers)), w,
info.getType()));
}
}
} catch (IOException ignored) {
}
}
} catch (IOException ignored) {
}
return ret;
}

/**
* Set the filter regexp of .jar names which we have to consider.
*
* default is '.*touchkit.*'
*/
public CvalAddonsChecker setFilter(String regexp) {
filterPattern = regexp;
return this;
}

/*
* Change the license provider, only used in tests.
*/
protected CvalAddonsChecker setLicenseProvider(CvalServer p) {
cvalChecker.setLicenseProvider(p);
return this;
}

private void printAgplLicense(String name, String version) {
System.out.println(LINE + "\n"
+ getErrorMessage("agpl", name, computeMajorVersion(version))
+ "\n" + LINE);
}

private void printServerUnreachable(String name, String version) {
System.out.println(LINE
+ "\n"
+ getErrorMessage("unreachable", name,
computeMajorVersion(version)) + "\n" + LINE);
}

private void printValidLicense(CvalInfo info, String title, String version) {
String msg = info.getMessage();
if (msg == null) {
String key = "evaluation".equals(info.getType()) ? "evaluation"
: "valid";
msg = getErrorMessage(key, title, computeMajorVersion(version),
info.getLicensee());
}
System.out.println("\n" + LINE + "\n" + msg + "\n" + LINE + "\n");
}
}

+ 488
- 0
client-compiler/src/com/vaadin/tools/CvalChecker.java View File

@@ -0,0 +1,488 @@
/*
* 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.tools;

import static java.lang.Integer.parseInt;

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.Locale;
import java.util.ResourceBundle;
import java.util.prefs.Preferences;

import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;

/**
* 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.
*/
public final class CvalChecker {

/*
* Class used for binding the JSON gotten from server.
*
* It is not in a separate f le, so as it is easier to copy into any product
* which does not depend on vaadin core.
*
* We are using org.json in order not to use additional dependency like
* auto-beans, gson, etc.
*/
public static class CvalInfo {

public static class Product {
private JSONObject o;

public Product(JSONObject o) {
this.o = o;
}

public String getName() {
return get(o, "name", String.class);
}

public Integer getVersion() {
return get(o, "version", Integer.class);
}
}

@SuppressWarnings("unchecked")
private static <T> T get(JSONObject o, String k, Class<T> clz) {
Object ret = null;
try {
if (clz == String.class) {
ret = o.getString(k);
} else if (clz == JSONObject.class) {
ret = o.getJSONObject(k);
} else if (clz == Integer.class) {
ret = o.getInt(k);
} else if (clz == Date.class) {
ret = new Date(o.getLong(k));
} else if (clz == Boolean.class) {
ret = o.getBoolean(k);
}
} catch (JSONException e) {
}
return (T) ret;
}

private static <T> T put(JSONObject o, String k, Object v) {
try {
o.put(k, v);
} catch (JSONException e) {
}
return null;
}

private JSONObject o;

private Product product;

public CvalInfo(JSONObject o) {
this.o = o;
product = new Product(get(o, "product", JSONObject.class));
}

public Boolean getExpired() {
return get(o, "expired", Boolean.class);
}

public Date getExpiredEpoch() {
return get(o, "expiredEpoch", Date.class);
}

public String getLicensee() {
return get(o, "licensee", String.class);
}

public String getLicenseKey() {
return get(o, "licenseKey", String.class);
}

public String getMessage() {
return get(o, "message", String.class);
}

public Product getProduct() {
return product;
}

public String getType() {
return get(o, "type", String.class);
}

public void setExpiredEpoch(Date expiredEpoch) {
put(o, "expiredEpoch", expiredEpoch.getTime());
}

public void setMessage(String msg) {
put(o, "message", msg);
}

@Override
public String toString() {
return o.toString();
}

public boolean isLicenseExpired() {
return (getExpired() != null && getExpired())
|| (getExpiredEpoch() != null && getExpiredEpoch().before(
new Date()));
}

public boolean isValidVersion(int majorVersion) {
return getProduct().getVersion() == null
|| getProduct().getVersion() >= majorVersion;

}

private boolean isValidInfo(String name, String key) {
return getProduct() != null && getProduct().getName() != null
&& getLicenseKey() != null
&& getProduct().getName().equals(name)
&& getLicenseKey().equals(key);
}
}

/*
* 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.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;
}
try {
JSONObject o = new JSONObject(json);
return new CvalInfo(o);
} catch (JSONException e) {
return null;
}
}

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
* @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.getExpiredEpoch() != null
&& !"evaluation".equals(info.getType())) {
long ts = info.getExpiredEpoch().getTime() + GRACE_DAYS_MSECS;
info.setExpiredEpoch(new Date(ts));
}

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 {
CvalInfo srvinfo = parseJson(provider.askServer(productName + "-"
+ productVersion, productKey, timeout));
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()) {
CvalInfo info = parseJson(json);
if (info != null) {
return info;
}
}
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 = "file://" + System.getProperty("user.home") + "/";
for (URL url : new URL[] { new URL(userHome + dotLicenseName),
new URL(userHome + licenseName),
URL.class.getResource("/" + dotLicenseName),
URL.class.getResource("/" + licenseName) }) {

if (url != null) {
try {
key = IOUtils.toString(url.openStream());
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);
}

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);
}
}

+ 13
- 0
client-compiler/src/com/vaadin/tools/CvalChecker.properties View File

@@ -0,0 +1,13 @@
expired={2} for {0} {1} has expired. Get a valid license at vaadin.com/pro

none=License for {0} {1} not found. Go to vaadin.com/pro for more details.

invalid=License for {0} {1} is not valid. Get a valid license from vaadin.com/pro

unreachable=License for {0} {1} has not been validated. Check your network connection.

evaluation= > Using an evaluation license for {0} {1}.

valid= > Using a valid license for {0} {1}.

agpl=Using AGPL version of {0} {1}. Commercial licensing options available at vaadin.com/pro

+ 184
- 0
client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java View File

@@ -0,0 +1,184 @@
/*
* 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.tools;

import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL;
import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL;
import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS;
import static com.vaadin.tools.CvalChecker.computeLicenseName;
import static com.vaadin.tools.CvalChecker.deleteCache;
import static com.vaadin.tools.CvalCheckerTest.VALID_KEY;
import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath;
import static com.vaadin.tools.CvalCheckerTest.cacheExists;
import static com.vaadin.tools.CvalCheckerTest.captureSystemOut;
import static com.vaadin.tools.CvalCheckerTest.productNameAgpl;
import static com.vaadin.tools.CvalCheckerTest.productNameApache;
import static com.vaadin.tools.CvalCheckerTest.productNameCval;
import static com.vaadin.tools.CvalCheckerTest.readSystemOut;
import static com.vaadin.tools.CvalCheckerTest.saveCache;
import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider;
import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider;

import java.util.List;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo;
import com.vaadin.tools.CvalChecker.InvalidCvalException;

/**
* The CvalAddonsChecker test.
*/
public class CvalAddonsCheckerTest {

CvalAddonsChecker addonChecker;
private String licenseName;

@Before
public void setup() {
addonChecker = new CvalAddonsChecker().setLicenseProvider(
validLicenseProvider).setFilter(".*test.*");
licenseName = computeLicenseName(productNameCval);

deleteCache(productNameCval);
System.getProperties().remove(licenseName);
}

@Test
public void testRunChecker() throws Exception {
// Create a product .jar with a cval license non required and add to our
// classpath
addLicensedJarToClasspath(productNameCval, VAADIN_CVAL);
// Remove other products in case other tests added them previously
addLicensedJarToClasspath(productNameAgpl, null);
addLicensedJarToClasspath(productNameApache, null);

// No license
// -> Break compilation
System.getProperties().remove(licenseName);
addonChecker.setLicenseProvider(validLicenseProvider);
try {
addonChecker.run();
Assert.fail();
} catch (InvalidCvalException expected) {
}
Assert.assertFalse(cacheExists(productNameCval));

// We have a license that has never been validated from the server and
// we are offline
// -> Show a message on compile time (“Your license for TouchKit 4 has
// not been validated.”)
System.setProperty(licenseName, VALID_KEY);
addonChecker.setLicenseProvider(unreachableLicenseProvider);
captureSystemOut();
addonChecker.run();
Assert.assertTrue(readSystemOut().contains("has not been validated"));
Assert.assertFalse(cacheExists(productNameCval));

// Valid license has previously been validated from the server and we
// are offline
// -> Use the cached server response
System.setProperty(licenseName, VALID_KEY);
addonChecker.setLicenseProvider(validLicenseProvider);
captureSystemOut();
addonChecker.run();
Assert.assertTrue(cacheExists(productNameCval));
addonChecker.setLicenseProvider(unreachableLicenseProvider);
addonChecker.run();

// Expired license and we are offline
// -> If it has expired less than 14 days ago, just work with no nag
// messages
System.setProperty(licenseName, VALID_KEY);
addonChecker.setLicenseProvider(unreachableLicenseProvider);
setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2),
"normal");
captureSystemOut();
addonChecker.run();

// Expired license and we are offline
// -> After 14 days, interpret it as expired license
setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS * 2),
"normal");
try {
addonChecker.run();
Assert.fail();
} catch (InvalidCvalException expected) {
}

// Invalid evaluation license
// -> Fail compilation with a message
// "Your evaluation license for TouchKit 4 is not valid"
System.setProperty(licenseName, VALID_KEY);
addonChecker.setLicenseProvider(unreachableLicenseProvider);
setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2),
"evaluation");
try {
addonChecker.run();
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertTrue(expected.getMessage().contains("expired"));
}

// Valid evaluation license
// -> The choice on whether to show the message is generated in
// widgetset
// compilation phase. No license checks are done in application runtime.
System.setProperty(licenseName, VALID_KEY);
addonChecker.setLicenseProvider(unreachableLicenseProvider);
setCacheFileTs(System.currentTimeMillis() + GRACE_DAYS_MSECS,
"evaluation");
List<CValUiInfo> uiInfo = addonChecker.run();
Assert.assertEquals(1, uiInfo.size());
Assert.assertEquals("Test " + productNameCval, uiInfo.get(0).product);
Assert.assertEquals("evaluation", uiInfo.get(0).type);

// Valid real license
// -> Work as expected
// -> Show info message “Using TouchKit 4 license
// 312-312321-321312-3-12-312-312
// licensed to <licensee> (1 developer license)”
System.setProperty(licenseName, VALID_KEY);
addonChecker.setLicenseProvider(validLicenseProvider);
captureSystemOut();
addonChecker.run();
Assert.assertTrue(readSystemOut().contains("valid"));
}

@Test
public void validateMultipleLicenses() throws Exception {
addLicensedJarToClasspath(productNameCval, VAADIN_CVAL);
addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL);
addLicensedJarToClasspath(productNameApache, "apache");

// We have a valid license for all products
System.setProperty(licenseName, VALID_KEY);
captureSystemOut();
addonChecker.run();
String out = readSystemOut();
Assert.assertTrue(out.contains("valid"));
Assert.assertTrue(out.contains("AGPL"));
Assert.assertTrue(cacheExists(productNameCval));
}

private void setCacheFileTs(long expireTs, String type) {
saveCache(productNameCval, null, false, expireTs, type);
}

}

+ 217
- 0
client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java View File

@@ -0,0 +1,217 @@
/*
* 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.tools;

import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL;
import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL;
import static com.vaadin.tools.CvalChecker.computeLicenseName;
import static com.vaadin.tools.CvalChecker.deleteCache;
import static com.vaadin.tools.CvalCheckerTest.INVALID_KEY;
import static com.vaadin.tools.CvalCheckerTest.VALID_KEY;
import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath;
import static com.vaadin.tools.CvalCheckerTest.cachedPreferences;
import static com.vaadin.tools.CvalCheckerTest.captureSystemOut;
import static com.vaadin.tools.CvalCheckerTest.expiredLicenseProvider;
import static com.vaadin.tools.CvalCheckerTest.productNameAgpl;
import static com.vaadin.tools.CvalCheckerTest.productNameCval;
import static com.vaadin.tools.CvalCheckerTest.readSystemOut;
import static com.vaadin.tools.CvalCheckerTest.restoreSystemOut;
import static com.vaadin.tools.CvalCheckerTest.saveCache;
import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider;
import static com.vaadin.tools.CvalCheckerTest.validEvaluationLicenseProvider;
import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.tools.CvalChecker.CvalServer;

/**
* Tests for Use Cases
*/
public class CvalAddonstCheckerUseCasesTest {

enum License {
NONE, EVAL, INVALID, REAL, EVAL_EXPIRED, REAL_EXPIRED
};

enum Version {
AGPL, CVAL
};

enum Validated {
YES, NO, OLD_KEY
};

enum Network {
ON, OFF
};

enum Compile {
YES, NO
};

enum Cached {
YES, NO
};

enum Message {
AGPL("AGPL"), VALID(">.* valid"), INVALID("not valid"), NO_LICENSE(
"not found"), NO_VALIDATED("has not been validated"), EXPIRED(
"has expired"), EVALUATION("evaluation");

String msg;

Message(String s) {
msg = s;
}
}

@Test
public void testUseCases() throws Exception {
useCase(1, License.NONE, Version.AGPL, Validated.NO, Network.OFF,
Compile.YES, Cached.NO, Message.AGPL);

useCase(2, License.NONE, Version.CVAL, Validated.NO, Network.ON,
Compile.NO, Cached.NO, Message.NO_LICENSE);

useCase(3, License.NONE, Version.CVAL, Validated.NO, Network.OFF,
Compile.NO, Cached.NO, Message.NO_LICENSE);

useCase(4, License.EVAL, Version.CVAL, Validated.NO, Network.ON,
Compile.YES, Cached.YES, Message.EVALUATION);

useCase(5, License.INVALID, Version.CVAL, Validated.NO, Network.OFF,
Compile.YES, Cached.NO, Message.NO_VALIDATED);

useCase(6, License.INVALID, Version.CVAL, Validated.NO, Network.ON,
Compile.NO, Cached.NO, Message.INVALID);

useCase(7, License.REAL, Version.CVAL, Validated.NO, Network.ON,
Compile.YES, Cached.YES, Message.VALID);

useCase(8, License.REAL, Version.CVAL, Validated.NO, Network.OFF,
Compile.YES, Cached.NO, Message.NO_VALIDATED);

useCase(9, License.REAL, Version.CVAL, Validated.YES, Network.OFF,
Compile.YES, Cached.YES, Message.VALID);

useCase(10, License.EVAL_EXPIRED, Version.CVAL, Validated.NO,
Network.ON, Compile.NO, Cached.YES, Message.EXPIRED);

useCase(11, License.EVAL_EXPIRED, Version.CVAL, Validated.YES,
Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED);

useCase(12, License.REAL_EXPIRED, Version.CVAL, Validated.YES,
Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED);

useCase(13, License.REAL_EXPIRED, Version.CVAL, Validated.NO,
Network.ON, Compile.NO, Cached.YES, Message.EXPIRED);

useCase(14, License.INVALID, Version.CVAL, Validated.OLD_KEY,
Network.OFF, Compile.YES, Cached.NO, Message.NO_VALIDATED);
}

@Test
public void testMultipleLicenseUseCases() throws Exception {
addLicensedJarToClasspath("test.foo", VAADIN_CVAL);
System.setProperty(computeLicenseName("test.foo"), VALID_KEY);

useCase(15, License.REAL, Version.CVAL, Validated.YES, Network.OFF,
Compile.YES, Cached.YES, Message.NO_VALIDATED);

useCase(16, License.REAL, Version.CVAL, Validated.YES, Network.ON,
Compile.NO, Cached.YES, Message.INVALID);
}

private void useCase(int number, License lic, Version ver, Validated val,
Network net, Compile res, Cached cached, Message msg)
throws Exception {

if (ver == Version.AGPL) {
addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL);
addLicensedJarToClasspath(productNameCval, null);
} else {
addLicensedJarToClasspath(productNameAgpl, null);
addLicensedJarToClasspath(productNameCval, VAADIN_CVAL);
}

String licenseName = computeLicenseName(productNameCval);

if (lic == License.NONE) {
System.getProperties().remove(licenseName);
} else if (lic == License.INVALID) {
System.setProperty(licenseName, INVALID_KEY);
} else {
System.setProperty(licenseName, VALID_KEY);
}

if (val == Validated.NO) {
deleteCache(productNameCval);
} else {
String type = lic == License.EVAL || lic == License.EVAL_EXPIRED ? "evaluation"
: null;
Boolean expired = lic == License.EVAL_EXPIRED
|| lic == License.REAL_EXPIRED ? true : null;
String key = val == Validated.OLD_KEY ? "oldkey" : null;
saveCache(productNameCval, key, expired, null, type);
}

CvalServer licenseProvider = validLicenseProvider;
if (net == Network.OFF) {
licenseProvider = unreachableLicenseProvider;
} else if (lic == License.EVAL_EXPIRED || lic == License.REAL_EXPIRED) {
licenseProvider = expiredLicenseProvider;
} else if (lic == License.EVAL) {
licenseProvider = validEvaluationLicenseProvider;
}

CvalAddonsChecker addonChecker = new CvalAddonsChecker();
addonChecker.setLicenseProvider(licenseProvider).setFilter(".*test.*");

captureSystemOut();

String testNumber = "Test #" + number + " ";
String message;
try {
addonChecker.run();
message = readSystemOut();
if (res == Compile.NO) {
Assert.fail(testNumber + "Exception not thrown:" + message);
}
} catch (Exception e) {
restoreSystemOut();
message = e.getMessage();
if (res == Compile.YES) {
Assert.fail(testNumber + "Unexpected Exception: "
+ e.getMessage());
}
}

// System.err.println("\n> " + testNumber + " " + lic + " " + ver + " "
// + val + " " + net + " " + res + " " + cached + "\n" + message);

Assert.assertTrue(testNumber + "Fail:\n" + message
+ "\nDoes not match:" + msg.msg,
message.matches("(?s).*" + msg.msg + ".*"));

String c = cachedPreferences(productNameCval);
Assert.assertTrue(testNumber + "Fail: cacheExists != "
+ (cached == Cached.YES) + "\n " + c,
(c != null) == (cached == Cached.YES));
}
}

+ 342
- 0
client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java View File

@@ -0,0 +1,342 @@
/*
* 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.tools;

import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_LICENSE;
import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_NAME;
import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_TITLE;
import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_VERSION;
import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS;
import static com.vaadin.tools.CvalChecker.cacheLicenseInfo;
import static com.vaadin.tools.CvalChecker.deleteCache;
import static com.vaadin.tools.CvalChecker.parseJson;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.prefs.Preferences;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.tools.CvalChecker.CvalInfo;
import com.vaadin.tools.CvalChecker.CvalServer;
import com.vaadin.tools.CvalChecker.InvalidCvalException;
import com.vaadin.tools.CvalChecker.UnreachableCvalServerException;

/**
* The CvalChecker test.
*/
public class CvalCheckerTest {

static final String productNameCval = "test.cval";
static final String productTitleCval = "Vaadin Test";
static final String productNameAgpl = "test.agpl";
static final String productTitleAgpl = "Vaadin Test";
static final String productNameApache = "test.apache";
static final String VALID_KEY = "valid";
static final String INVALID_KEY = "invalid";

static final String responseJson = "{'licenseKey':'" + VALID_KEY + "',"
+ "'licensee':'Test User','type':'normal',"
+ "'expiredEpoch':'1893511225000'," + "'product':{'name':'"
+ productNameCval + "', 'version': 2}}";

private static ByteArrayOutputStream outContent;

// A provider returning a valid license if productKey is valid or null if
// invalid
static final CvalServer validLicenseProvider = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout) {
return VALID_KEY.equals(productKey) ? responseJson : null;
}
};
// A provider returning a valid evaluation license
static final CvalServer validEvaluationLicenseProvider = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout) {
return responseJson.replace("normal", "evaluation");
}
};
// A provider returning an expired license with a server message
static final CvalServer expiredLicenseProviderWithMessage = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout) {
return responseJson
.replace("'expired",
"'message':'Custom\\\\nServer\\\\nMessage','expired':true,'expired");
}
};
// A provider returning an expired license with a server message
static final CvalServer expiredLicenseProvider = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout) {
return responseJson.replace("'expired", "'expired':true,'expired");
}
};
// A provider returning an expired epoch license
static final CvalServer expiredEpochLicenseProvider = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout) {
long ts = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000;
return responseJson.replace("1893511225000", "" + ts);
}
};
// A provider returning an unlimited license
static final CvalServer unlimitedLicenseProvider = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout) {
return responseJson.replaceFirst("1893511225000", "");
}
};
// An unreachable provider
static final CvalServer unreachableLicenseProvider = new CvalServer() {
@Override
String askServer(String productName, String productKey, int timeout)
throws IOException {
// Normally there is no route for this ip in public routers, so we
// should get a timeout.
licenseUrl = "http://localhost:9999/";
return super.askServer(productName, productKey, 1000);
}
};

private CvalChecker licenseChecker;
private String licenseName;

@Before
public void setup() {
licenseChecker = new CvalChecker()
.setLicenseProvider(validLicenseProvider);
licenseName = CvalChecker.computeLicenseName(productNameCval);
System.getProperties().remove(licenseName);
deleteCache(productNameCval);
}

@Test
public void testValidateProduct() throws Exception {
deleteCache(productNameCval);

// If the license key in our environment is null, throw an exception
try {
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertFalse(cacheExists(productNameCval));

// If the license key is empty, throw an exception
System.setProperty(licenseName, "");
try {
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertFalse(cacheExists(productNameCval));

// If license key is invalid, throw an exception
System.setProperty(licenseName, "invalid");
try {
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertFalse(cacheExists(productNameCval));

// Fail if version is bigger
System.setProperty(licenseName, VALID_KEY);
try {
licenseChecker.validateProduct(productNameCval, "3.0",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertFalse(cacheExists(productNameCval));

// Success if license key and version are valid
System.setProperty(licenseName, VALID_KEY);
licenseChecker
.validateProduct(productNameCval, "2.1", productTitleCval);
Assert.assertTrue(cacheExists(productNameCval));

// Success if license and cache file are valid, although the license
// server is offline
licenseChecker.setLicenseProvider(unreachableLicenseProvider);
licenseChecker
.validateProduct(productNameCval, "2.1", productTitleCval);
Assert.assertTrue(cacheExists(productNameCval));

// Fail if license key changes although cache file were validated
// previously and it is ok, we are offline
try {
System.setProperty(licenseName, INVALID_KEY);
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.fail();
} catch (UnreachableCvalServerException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertFalse(cacheExists(productNameCval));

// Fail with unreachable exception if license has never verified and
// server is offline
try {
System.setProperty(licenseName, VALID_KEY);
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.fail();
} catch (UnreachableCvalServerException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertFalse(cacheExists(productNameCval));

// Fail when expired flag comes in the server response, although the
// expired is valid.
deleteCache(productNameCval);
licenseChecker.setLicenseProvider(expiredLicenseProviderWithMessage);
try {
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertEquals(productNameCval, expected.name);
// Check that we use server customized message if it comes
Assert.assertTrue(expected.getMessage().contains("Custom"));
}
Assert.assertTrue(cacheExists(productNameCval));

// Check an unlimited license
licenseChecker.setLicenseProvider(unlimitedLicenseProvider);
licenseChecker
.validateProduct(productNameCval, "2.1", productTitleCval);
Assert.assertTrue(cacheExists(productNameCval));

// Fail if expired flag does not come, but expired epoch is in the past
System.setProperty(licenseName, VALID_KEY);
deleteCache(productNameCval);
licenseChecker.setLicenseProvider(expiredEpochLicenseProvider);
try {
licenseChecker.validateProduct(productNameCval, "2.1",
productTitleCval);
Assert.fail();
} catch (InvalidCvalException expected) {
Assert.assertEquals(productNameCval, expected.name);
}
Assert.assertTrue(cacheExists(productNameCval));
}

/*
* Creates a new .jar file with a MANIFEST.MF with all vaadin license info
* attributes set, and add the .jar to the classpath
*/
static void addLicensedJarToClasspath(String productName, String licenseType)
throws Exception {
// Create a manifest with Vaadin CVAL license
Manifest testManifest = new Manifest();
testManifest.getMainAttributes().putValue("Manifest-Version", "1.0");
testManifest.getMainAttributes().putValue(VAADIN_ADDON_LICENSE,
licenseType);
testManifest.getMainAttributes().putValue(VAADIN_ADDON_NAME,
productName);
testManifest.getMainAttributes().putValue(VAADIN_ADDON_TITLE,
"Test " + productName);
testManifest.getMainAttributes().putValue(VAADIN_ADDON_VERSION, "2");

// Create a temporary Jar
String tmpDir = System.getProperty("java.io.tmpdir");
File testJarFile = new File(tmpDir + "vaadin." + productName + ".jar");
testJarFile.deleteOnExit();
JarOutputStream target = new JarOutputStream(new FileOutputStream(
testJarFile), testManifest);
target.close();

// Add the new jar to our classpath (use reflection)
URL url = new URL("file://" + testJarFile.getAbsolutePath());
final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL",
new Class[] { URL.class });
addURL.setAccessible(true);
final URLClassLoader urlClassLoader = (URLClassLoader) Thread
.currentThread().getContextClassLoader();
addURL.invoke(urlClassLoader, new Object[] { url });
}

static boolean cacheExists(String productName) {
return cachedPreferences(productName) != null;
}

static String cachedPreferences(String productName) {
// ~/Library/Preferences/com.apple.java.util.prefs.plist
// .java/.userPrefs/com/google/gwt/dev/shell/prefs.xml
// HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs
Preferences p = Preferences.userNodeForPackage(CvalInfo.class);
return p.get(productName, null);
}

static void saveCache(String productName, String key, Boolean expired,
Long expireTs, String type) {
String json = responseJson.replace(productNameCval, productName);
if (expired != null && expired) {
expireTs = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000;
}
if (expireTs != null) {
json = json.replace("1893511225000", "" + expireTs);
}
if (key != null) {
json = json.replace(VALID_KEY, key);
}
if (type != null) {
json = json.replace("normal", type);

}
cacheLicenseInfo(parseJson(json));
}

static void captureSystemOut() {
outContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(outContent));
}

static String readSystemOut() {
restoreSystemOut();
return outContent.toString();
}

static void restoreSystemOut() {
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
}
}

+ 48
- 3
client/src/com/vaadin/client/metadata/ConnectorBundleLoader.java View File

@@ -1,12 +1,12 @@
/*
* 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
@@ -15,13 +15,35 @@
*/
package com.vaadin.client.metadata;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.shared.GWT;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.FastStringMap;
import com.vaadin.client.metadata.AsyncBundleLoader.State;
import com.vaadin.client.ui.VNotification;
import com.vaadin.shared.Position;

public abstract class ConnectorBundleLoader {

public static class CValUiInfo {
public final String widgetset;
public final String product;
public final String version;
public final String type;

public CValUiInfo(String product, String version, String widgetset,
String type) {
this.product = product;
this.version = version;
this.widgetset = widgetset;
this.type = type;
}
}

public static final String EAGER_BUNDLE_NAME = "__eager";
public static final String DEFERRED_BUNDLE_NAME = "__deferred";

@@ -113,4 +135,27 @@ public abstract class ConnectorBundleLoader {

public abstract void init();

protected List<CValUiInfo> cvals = new ArrayList<CValUiInfo>();

public void cval(String typeName) {
if (!cvals.isEmpty()) {
String msg = "";
for (CValUiInfo c : cvals) {
String ns = c.widgetset.replaceFirst("\\.[^\\.]+$", "");
if (typeName.startsWith(ns)) {
cvals.remove(c);
msg += c.product + " " + c.version + "<br/>";
}
}
if (!msg.isEmpty()) {
// We need a widget for using VNotification, using the
// context-menu parent. Is there an easy way?
Widget w = ApplicationConfiguration.getRunningApplications()
.get(0).getContextMenu().getParent();
VNotification n = VNotification.createNotification(0, w);
n.setWidget(new HTML("Using Evaluation License of:<br/>" + msg));
n.show(Position.BOTTOM_RIGHT);
}
}
}
}

+ 11
- 6
client/src/com/vaadin/client/metadata/Type.java View File

@@ -1,12 +1,12 @@
/*
* 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
@@ -19,6 +19,7 @@ import java.util.Collection;

import com.google.gwt.core.client.JsArrayString;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.communication.JSONSerializer;

public class Type {
@@ -47,7 +48,11 @@ public class Type {

public Object createInstance() throws NoDataException {
Invoker invoker = TypeDataStore.getConstructor(this);
return invoker.invoke(null);
Object ret = invoker.invoke(null);
if (ret instanceof ServerConnector) {
ConnectorBundleLoader.get().cval(name);
}
return ret;
}

public Method getMethod(String name) {
@@ -57,7 +62,7 @@ public class Type {
/**
* @return
* @throws NoDataException
*
*
* @deprecated As of 7.0.1, use {@link #getPropertiesAsArray()} instead for
* improved performance
*/
@@ -96,7 +101,7 @@ public class Type {
* returned string may change without notice and should not be used for any
* other purpose than identification. The signature is currently based on
* the fully qualified name of the type.
*
*
* @return the unique signature of this type
*/
public String getSignature() {

Loading…
Cancel
Save