diff --git a/plugin-api/.gitignore b/plugin-api/.gitignore
new file mode 100644
index 00000000000..ab956abf6f9
--- /dev/null
+++ b/plugin-api/.gitignore
@@ -0,0 +1 @@
+/eclipse-build/
diff --git a/plugin-api/pom.xml b/plugin-api/pom.xml
new file mode 100644
index 00000000000..e5943d9932f
--- /dev/null
+++ b/plugin-api/pom.xml
@@ -0,0 +1,67 @@
+
+
+
+ elasticsearch-license
+ org.elasticsearch
+ 2.0.0-SNAPSHOT
+
+ 4.0.0
+
+ elasticsearch-license-plugin-api
+
+ ${project.parent.basedir}
+
+
+
+
+
+ org.elasticsearch
+ elasticsearch-license-licensor
+ 2.0.0-SNAPSHOT
+ test
+
+
+ org.elasticsearch
+ elasticsearch-license-core
+ 2.0.0-SNAPSHOT
+ compile
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+
+
+ com.carrotsearch.randomizedtesting
+ junit4-maven-plugin
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugin-api/src/main/java/org/elasticsearch/license/plugin/LicenseVersion.java b/plugin-api/src/main/java/org/elasticsearch/license/plugin/LicenseVersion.java
new file mode 100644
index 00000000000..4e2cad0d5cd
--- /dev/null
+++ b/plugin-api/src/main/java/org/elasticsearch/license/plugin/LicenseVersion.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.plugin;
+
+import org.elasticsearch.Version;
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.license.core.License;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+@SuppressWarnings("deprecation")
+public class LicenseVersion implements Serializable {
+
+ // The logic for ID is: XXYYZZAA, where XX is major version, YY is minor version, ZZ is revision, and AA is Beta/RC indicator
+ // AA values below 50 are beta builds, and below 99 are RC builds, with 99 indicating a release
+ // the (internal) format of the id is there so we can easily do after/before checks on the id
+
+ public static final int V_1_0_0_ID = /*00*/1000099;
+ public static final int V_2_0_0_ID = /*00*/2000099;
+ public static final LicenseVersion V_1_0_0 = new LicenseVersion(V_1_0_0_ID, false, License.VERSION_START, Version.V_1_4_0_Beta1);
+ public static final LicenseVersion V_2_0_0 = new LicenseVersion(V_2_0_0_ID, true, License.VERSION_START, Version.V_2_0_0);
+
+ public static final LicenseVersion CURRENT = V_2_0_0;
+
+ public static LicenseVersion readVersion(StreamInput in) throws IOException {
+ return fromId(in.readVInt());
+ }
+
+ public static LicenseVersion fromId(int id) {
+ switch (id) {
+ case V_1_0_0_ID:
+ return V_1_0_0;
+
+ default:
+ return new LicenseVersion(id, null, License.VERSION_CURRENT, Version.CURRENT);
+ }
+ }
+
+ public static void writeVersion(LicenseVersion version, StreamOutput out) throws IOException {
+ out.writeVInt(version.id);
+ }
+
+ /**
+ * Returns the smallest version between the 2.
+ */
+ public static LicenseVersion smallest(LicenseVersion version1, LicenseVersion version2) {
+ return version1.id < version2.id ? version1 : version2;
+ }
+
+ /**
+ * Returns the version given its string representation, current version if the argument is null or empty
+ */
+ public static LicenseVersion fromString(String version) {
+ if (!Strings.hasLength(version)) {
+ return LicenseVersion.CURRENT;
+ }
+
+ String[] parts = version.split("\\.");
+ if (parts.length < 3 || parts.length > 4) {
+ throw new IllegalArgumentException("the version needs to contain major, minor and revision, and optionally the build");
+ }
+
+ try {
+ //we reverse the version id calculation based on some assumption as we can't reliably reverse the modulo
+ int major = Integer.parseInt(parts[0]) * 1000000;
+ int minor = Integer.parseInt(parts[1]) * 10000;
+ int revision = Integer.parseInt(parts[2]) * 100;
+
+ int build = 99;
+ if (parts.length == 4) {
+ String buildStr = parts[3];
+ if (buildStr.startsWith("Beta")) {
+ build = Integer.parseInt(buildStr.substring(4));
+ }
+ if (buildStr.startsWith("RC")) {
+ build = Integer.parseInt(buildStr.substring(2)) + 50;
+ }
+ }
+
+ return fromId(major + minor + revision + build);
+
+ } catch(NumberFormatException e) {
+ throw new IllegalArgumentException("unable to parse version " + version, e);
+ }
+ }
+
+ public final int id;
+ public final byte major;
+ public final byte minor;
+ public final byte revision;
+ public final byte build;
+ public final Boolean snapshot;
+ public final int minSignatureVersion;
+ public final Version minEsCompatibilityVersion;
+
+ LicenseVersion(int id, @Nullable Boolean snapshot, int minSignatureVersion, Version minEsCompatibilityVersion) {
+ this.id = id;
+ this.major = (byte) ((id / 1000000) % 100);
+ this.minor = (byte) ((id / 10000) % 100);
+ this.revision = (byte) ((id / 100) % 100);
+ this.build = (byte) (id % 100);
+ this.snapshot = snapshot;
+ this.minSignatureVersion = minSignatureVersion;
+ this.minEsCompatibilityVersion = minEsCompatibilityVersion;
+ }
+
+ public boolean snapshot() {
+ return snapshot != null && snapshot;
+ }
+
+ public boolean after(LicenseVersion version) {
+ return version.id < id;
+ }
+
+ public boolean before(LicenseVersion version) {
+ return version.id > id;
+ }
+
+ /**
+ * Returns the minimum compatible version based on the current
+ * version. Ie a node needs to have at least the return version in order
+ * to communicate with a node running the current version. The returned version
+ * is in most of the cases the smallest major version release unless the current version
+ * is a beta or RC release then the version itself is returned.
+ */
+ public LicenseVersion minimumCompatibilityVersion() {
+ return LicenseVersion.smallest(this, fromId(major * 1000000 + 99));
+ }
+
+ /**
+ * @return The minimum elasticsearch version this license version is compatible with.
+ */
+ public Version minimumEsCompatiblityVersion() {
+ return minEsCompatibilityVersion;
+ }
+
+ /**
+ * @return The minimum license signature version this license plugin is compatible with.
+ */
+ public int minimumSignatureVersion() {
+ return minSignatureVersion;
+ }
+
+ /**
+ * Just the version number (without -SNAPSHOT if snapshot).
+ */
+ public String number() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(major).append('.').append(minor).append('.').append(revision);
+ if (build < 50) {
+ sb.append(".Beta").append(build);
+ } else if (build < 99) {
+ sb.append(".RC").append(build - 50);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(number());
+ if (snapshot()) {
+ sb.append("-SNAPSHOT");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LicenseVersion that = (LicenseVersion) o;
+
+ if (id != that.id) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id;
+ }
+}
diff --git a/plugin-api/src/main/java/org/elasticsearch/license/plugin/core/LicenseExpiredException.java b/plugin-api/src/main/java/org/elasticsearch/license/plugin/core/LicenseExpiredException.java
new file mode 100644
index 00000000000..4fbbe351804
--- /dev/null
+++ b/plugin-api/src/main/java/org/elasticsearch/license/plugin/core/LicenseExpiredException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.plugin.core;
+
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.rest.RestStatus;
+
+/**
+ * Exception to be thrown when a feature action requires a valid license
+ */
+public class LicenseExpiredException extends ElasticsearchException {
+
+ private final String feature;
+
+ public LicenseExpiredException(String feature) {
+ super("license expired for feature [" + feature + "]");
+ this.feature = feature;
+ }
+
+ @Override
+ public RestStatus status() {
+ return RestStatus.UNAUTHORIZED;
+ }
+
+ public String feature() {
+ return feature;
+ }
+}
diff --git a/plugin-api/src/main/java/org/elasticsearch/license/plugin/core/LicensesClientService.java b/plugin-api/src/main/java/org/elasticsearch/license/plugin/core/LicensesClientService.java
new file mode 100644
index 00000000000..548e87d72ea
--- /dev/null
+++ b/plugin-api/src/main/java/org/elasticsearch/license/plugin/core/LicensesClientService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.plugin.core;
+
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.license.core.License;
+
+import java.util.Collection;
+
+
+//@ImplementedBy(LicensesService.class)
+public interface LicensesClientService {
+
+ public interface Listener {
+
+ /**
+ * Called to enable a feature
+ */
+ public void onEnabled(License license);
+
+ /**
+ * Called to disable a feature
+ */
+ public void onDisabled(License license);
+
+ }
+
+ /**
+ * Registers a feature for licensing
+ *
+ * @param feature - name of the feature to register (must be in sync with license Generator feature name)
+ * @param trialLicenseOptions - Trial license specification used to generate a one-time trial license for the feature;
+ * use null
if no trial license should be generated for the feature
+ * @param expirationCallbacks - A collection of Pre and/or Post expiration callbacks
+ * @param listener - used to notify on feature enable/disable
+ */
+ void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection expirationCallbacks, Listener listener);
+
+ public static class TrialLicenseOptions {
+ final TimeValue duration;
+ final int maxNodes;
+
+ public TrialLicenseOptions(TimeValue duration, int maxNodes) {
+ this.duration = duration;
+ this.maxNodes = maxNodes;
+ }
+ }
+
+
+ public static interface LicenseCallback {
+ void on(License license, ExpirationStatus status);
+ }
+
+ public static abstract class ExpirationCallback implements LicenseCallback {
+
+ public enum Orientation { PRE, POST }
+
+ public static abstract class Pre extends ExpirationCallback {
+
+ /**
+ * Callback schedule prior to license expiry
+ *
+ * @param min latest relative time to execute before license expiry
+ * @param max earliest relative time to execute before license expiry
+ * @param frequency interval between execution
+ */
+ public Pre(TimeValue min, TimeValue max, TimeValue frequency) {
+ super(Orientation.PRE, min, max, frequency);
+ }
+
+ @Override
+ public boolean matches(long expirationDate, long now) {
+ long expiryDuration = expirationDate - now;
+ if (expiryDuration > 0l) {
+ if (expiryDuration <= max().getMillis()) {
+ return expiryDuration >= min().getMillis();
+ }
+ }
+ return false;
+ }
+ }
+
+ public static abstract class Post extends ExpirationCallback {
+
+ /**
+ * Callback schedule after license expiry
+ *
+ * @param min earliest relative time to execute after license expiry
+ * @param max latest relative time to execute after license expiry
+ * @param frequency interval between execution
+ */
+ public Post(TimeValue min, TimeValue max, TimeValue frequency) {
+ super(Orientation.POST, min, max, frequency);
+ }
+
+ @Override
+ public boolean matches(long expirationDate, long now) {
+ long postExpiryDuration = now - expirationDate;
+ if (postExpiryDuration > 0l) {
+ if (postExpiryDuration <= max().getMillis()) {
+ return postExpiryDuration >= min().getMillis();
+ }
+ }
+ return false;
+ }
+ }
+
+ private final Orientation orientation;
+ private final TimeValue min;
+ private final TimeValue max;
+ private final TimeValue frequency;
+
+ private ExpirationCallback(Orientation orientation, TimeValue min, TimeValue max, TimeValue frequency) {
+ this.orientation = orientation;
+ this.min = (min == null) ? TimeValue.timeValueMillis(0) : min;
+ this.max = (max == null) ? TimeValue.timeValueMillis(Long.MAX_VALUE) : max;
+ this.frequency = frequency;
+ if (frequency == null) {
+ throw new IllegalArgumentException("frequency can not be null");
+ }
+ }
+
+ public Orientation orientation() {
+ return orientation;
+ }
+
+ public TimeValue min() {
+ return min;
+ }
+
+ public TimeValue max() {
+ return max;
+ }
+
+ public TimeValue frequency() {
+ return frequency;
+ }
+
+ public abstract boolean matches(long expirationDate, long now);
+ }
+
+ public static class ExpirationStatus {
+ private final boolean expired;
+ private final TimeValue time;
+
+ ExpirationStatus(boolean expired, TimeValue time) {
+ this.expired = expired;
+ this.time = time;
+ }
+
+ public boolean expired() {
+ return expired;
+ }
+
+ public TimeValue time() {
+ return time;
+ }
+ }
+
+
+}
diff --git a/plugin/.gitignore b/plugin/.gitignore
new file mode 100644
index 00000000000..ab956abf6f9
--- /dev/null
+++ b/plugin/.gitignore
@@ -0,0 +1 @@
+/eclipse-build/
diff --git a/plugin/pom.xml b/plugin/pom.xml
index ba579d18649..9ba064ca882 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -12,7 +12,8 @@
elasticsearch-license-plugin
-
+
${basedir}/src/test/resources
${project.parent.basedir}
@@ -30,6 +31,12 @@
2.0.0-SNAPSHOT
compile
+
+ org.elasticsearch
+ elasticsearch-license-plugin-api
+ 2.0.0-SNAPSHOT
+ compile
+
diff --git a/plugin/src/main/java/org/elasticsearch/license/plugin/LicenseModule.java b/plugin/src/main/java/org/elasticsearch/license/plugin/LicenseModule.java
index dc9c4df64b3..4ec46ec33f0 100644
--- a/plugin/src/main/java/org/elasticsearch/license/plugin/LicenseModule.java
+++ b/plugin/src/main/java/org/elasticsearch/license/plugin/LicenseModule.java
@@ -8,6 +8,7 @@ package org.elasticsearch.license.plugin;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Scopes;
import org.elasticsearch.license.core.LicenseVerifier;
+import org.elasticsearch.license.plugin.core.LicensesClientService;
import org.elasticsearch.license.plugin.core.LicensesService;
public class LicenseModule extends AbstractModule {
@@ -15,5 +16,6 @@ public class LicenseModule extends AbstractModule {
protected void configure() {
bind(LicenseVerifier.class).in(Scopes.SINGLETON);
bind(LicensesService.class).in(Scopes.SINGLETON);
+ bind(LicensesClientService.class).to(LicensesService.class).in(Scopes.SINGLETON);
}
}
diff --git a/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesClientService.java b/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesClientService.java
deleted file mode 100644
index eb289cdb0e4..00000000000
--- a/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesClientService.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-package org.elasticsearch.license.plugin.core;
-
-import org.elasticsearch.common.inject.ImplementedBy;
-import org.elasticsearch.license.core.License;
-
-import java.util.Collection;
-
-import static org.elasticsearch.license.plugin.core.LicensesService.*;
-import static org.elasticsearch.license.plugin.core.LicensesService.TrialLicenseOptions;
-
-@ImplementedBy(LicensesService.class)
-public interface LicensesClientService {
-
- public interface Listener {
-
- /**
- * Called to enable a feature
- */
- public void onEnabled(License license);
-
- /**
- * Called to disable a feature
- */
- public void onDisabled(License license);
-
- }
-
- /**
- * Registers a feature for licensing
- *
- * @param feature - name of the feature to register (must be in sync with license Generator feature name)
- * @param trialLicenseOptions - Trial license specification used to generate a one-time trial license for the feature;
- * use null
if no trial license should be generated for the feature
- * @param expirationCallbacks - A collection of Pre and/or Post expiration callbacks
- * @param listener - used to notify on feature enable/disable
- */
- void register(String feature, TrialLicenseOptions trialLicenseOptions, Collection expirationCallbacks, Listener listener);
-}
diff --git a/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java
index 70c1d7e306b..ba638ab83bd 100644
--- a/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java
+++ b/plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java
@@ -741,125 +741,8 @@ public class LicensesService extends AbstractLifecycleComponent
}
}
- public static class TrialLicenseOptions {
- final TimeValue duration;
- final int maxNodes;
- public TrialLicenseOptions(TimeValue duration, int maxNodes) {
- this.duration = duration;
- this.maxNodes = maxNodes;
- }
- }
- public static class ExpirationStatus {
- private final boolean expired;
- private final TimeValue time;
-
- private ExpirationStatus(boolean expired, TimeValue time) {
- this.expired = expired;
- this.time = time;
- }
-
- public boolean expired() {
- return expired;
- }
-
- public TimeValue time() {
- return time;
- }
- }
-
- public static interface LicenseCallback {
- void on(License license, ExpirationStatus status);
- }
-
- public static abstract class ExpirationCallback implements LicenseCallback {
-
- public enum Orientation { PRE, POST }
-
- public static abstract class Pre extends ExpirationCallback {
-
- /**
- * Callback schedule prior to license expiry
- *
- * @param min latest relative time to execute before license expiry
- * @param max earliest relative time to execute before license expiry
- * @param frequency interval between execution
- */
- public Pre(TimeValue min, TimeValue max, TimeValue frequency) {
- super(Orientation.PRE, min, max, frequency);
- }
-
- @Override
- public boolean matches(long expirationDate, long now) {
- long expiryDuration = expirationDate - now;
- if (expiryDuration > 0l) {
- if (expiryDuration <= max().getMillis()) {
- return expiryDuration >= min().getMillis();
- }
- }
- return false;
- }
- }
-
- public static abstract class Post extends ExpirationCallback {
-
- /**
- * Callback schedule after license expiry
- *
- * @param min earliest relative time to execute after license expiry
- * @param max latest relative time to execute after license expiry
- * @param frequency interval between execution
- */
- public Post(TimeValue min, TimeValue max, TimeValue frequency) {
- super(Orientation.POST, min, max, frequency);
- }
-
- @Override
- public boolean matches(long expirationDate, long now) {
- long postExpiryDuration = now - expirationDate;
- if (postExpiryDuration > 0l) {
- if (postExpiryDuration <= max().getMillis()) {
- return postExpiryDuration >= min().getMillis();
- }
- }
- return false;
- }
- }
-
- private final Orientation orientation;
- private final TimeValue min;
- private final TimeValue max;
- private final TimeValue frequency;
-
- private ExpirationCallback(Orientation orientation, TimeValue min, TimeValue max, TimeValue frequency) {
- this.orientation = orientation;
- this.min = (min == null) ? TimeValue.timeValueMillis(0) : min;
- this.max = (max == null) ? TimeValue.timeValueMillis(Long.MAX_VALUE) : max;
- this.frequency = frequency;
- if (frequency == null) {
- throw new IllegalArgumentException("frequency can not be null");
- }
- }
-
- public Orientation orientation() {
- return orientation;
- }
-
- public TimeValue min() {
- return min;
- }
-
- public TimeValue max() {
- return max;
- }
-
- public TimeValue frequency() {
- return frequency;
- }
-
- public abstract boolean matches(long expirationDate, long now);
- }
/**
* Stores configuration and listener for a feature
@@ -993,6 +876,7 @@ public class LicensesService extends AbstractLifecycleComponent
}
}
+ @Override
public String toString() {
return "(feature: " + feature + ", enabled: " + enabled + ")";
}
diff --git a/pom.xml b/pom.xml
index b48a978ed53..d1884dd5614 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,7 @@
core
licensor
plugin
+ plugin-api