diff --git a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java
index c5c037d7653..23363ce9452 100644
--- a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java
+++ b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/Graph.java
@@ -46,9 +46,6 @@ public class Graph extends Plugin implements ActionPlugin {
     public Collection<Module> createGuiceModules() {
         return Collections.singletonList(b -> {
             XPackPlugin.bindFeatureSet(b, GraphFeatureSet.class);
-            if (transportClientMode) {
-                b.bind(GraphLicensee.class).toProvider(Providers.of(null));
-            }
         });
     }
 
diff --git a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java
index 2cba904304c..6f350bd9ceb 100644
--- a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java
+++ b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphFeatureSet.java
@@ -10,6 +10,7 @@ import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.XPackFeatureSet;
 
 import java.io.IOException;
@@ -20,12 +21,12 @@ import java.io.IOException;
 public class GraphFeatureSet implements XPackFeatureSet {
 
     private final boolean enabled;
-    private final GraphLicensee licensee;
+    private final XPackLicenseState licenseState;
 
     @Inject
-    public GraphFeatureSet(Settings settings, @Nullable GraphLicensee licensee, NamedWriteableRegistry namedWriteableRegistry) {
+    public GraphFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState, NamedWriteableRegistry namedWriteableRegistry) {
         this.enabled = Graph.enabled(settings);
-        this.licensee = licensee;
+        this.licenseState = licenseState;
         namedWriteableRegistry.register(Usage.class, Usage.writeableName(Graph.NAME), Usage::new);
     }
 
@@ -41,7 +42,7 @@ public class GraphFeatureSet implements XPackFeatureSet {
 
     @Override
     public boolean available() {
-        return licensee != null && licensee.isAvailable();
+        return licenseState != null && licenseState.isGraphAllowed();
     }
 
     @Override
diff --git a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java
deleted file mode 100644
index 1468b63acbd..00000000000
--- a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java
+++ /dev/null
@@ -1,64 +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.xpack.graph;
-
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
-
-public class GraphLicensee extends AbstractLicenseeComponent {
-
-    public static final String ID = Graph.NAME;
-
-    public GraphLicensee(Settings settings) {
-        super(settings, ID);
-    }
-
-    @Override
-    public String[] expirationMessages() {
-        return new String[] {
-                "Graph explore APIs are disabled"
-        };
-    }
-
-    @Override
-    public String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode) {
-        switch (newMode) {
-            case BASIC:
-            case STANDARD:
-            case GOLD:
-                switch (currentMode) {
-                    case TRIAL:
-                    case PLATINUM:
-                        return new String[] { "Graph will be disabled" };
-                }
-                break;
-        }
-        return Strings.EMPTY_ARRAY;
-    }
-
-    /**
-     * Determine if Graph Exploration should be enabled.
-     * <p>
-     * Exploration is only disabled when the license has expired or if the mode is not:
-     * <ul>
-     * <li>{@link OperationMode#PLATINUM}</li>
-     * <li>{@link OperationMode#TRIAL}</li>
-     * </ul>
-     *
-     * @return {@code true} as long as the license is valid. Otherwise {@code false}.
-     */
-    public boolean isAvailable() {
-        // status is volatile
-        Status localStatus = status;
-        OperationMode operationMode = localStatus.getMode();
-
-        boolean licensed = operationMode == OperationMode.TRIAL || operationMode == OperationMode.PLATINUM;
-
-        return licensed && localStatus.isActive();
-    }
-}
diff --git a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/action/TransportGraphExploreAction.java b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/action/TransportGraphExploreAction.java
index 2d58ac38282..8312c23f519 100644
--- a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/action/TransportGraphExploreAction.java
+++ b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/action/TransportGraphExploreAction.java
@@ -24,6 +24,7 @@ import org.elasticsearch.common.util.CollectionUtils;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.license.plugin.core.LicenseUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.bucket.sampler.DiversifiedAggregationBuilder;
@@ -37,7 +38,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude
 import org.elasticsearch.search.builder.SearchSourceBuilder;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
-import org.elasticsearch.xpack.graph.GraphLicensee;
+import org.elasticsearch.xpack.graph.Graph;
 import org.elasticsearch.xpack.graph.action.Connection.ConnectionId;
 import org.elasticsearch.xpack.graph.action.GraphExploreRequest.TermBoost;
 import org.elasticsearch.xpack.graph.action.Vertex.VertexId;
@@ -58,7 +59,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 public class TransportGraphExploreAction extends HandledTransportAction<GraphExploreRequest, GraphExploreResponse> {
 
     private final TransportSearchAction searchAction;
-    protected final GraphLicensee licensee;
+    protected final XPackLicenseState licenseState;
 
     static class VertexPriorityQueue extends PriorityQueue<Vertex> {
 
@@ -76,19 +77,19 @@ public class TransportGraphExploreAction extends HandledTransportAction<GraphExp
     @Inject
     public TransportGraphExploreAction(Settings settings, ThreadPool threadPool, TransportSearchAction transportSearchAction,
             TransportService transportService, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
-            GraphLicensee licensee) {
+            XPackLicenseState licenseState) {
         super(settings, GraphExploreAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, 
                 GraphExploreRequest::new);
         this.searchAction = transportSearchAction;
-        this.licensee = licensee;
+        this.licenseState = licenseState;
     }
 
     @Override
     protected void doExecute(GraphExploreRequest request, ActionListener<GraphExploreResponse> listener) {
-        if (licensee.isAvailable()) {
+        if (licenseState.isGraphAllowed()) {
             new AsyncGraphAction(request, listener).start();
         } else {
-            listener.onFailure(LicenseUtils.newComplianceException(GraphLicensee.ID));
+            listener.onFailure(LicenseUtils.newComplianceException(Graph.NAME));
         }  
     }
 
diff --git a/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java b/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java
index 56d3b949bbe..8c612d17c57 100644
--- a/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java
+++ b/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/GraphFeatureSetTests.java
@@ -7,9 +7,8 @@ package org.elasticsearch.xpack.graph;
 
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
-import org.elasticsearch.xpack.graph.GraphFeatureSet;
-import org.elasticsearch.xpack.graph.GraphLicensee;
 import org.junit.Before;
 
 import static org.hamcrest.core.Is.is;
@@ -24,24 +23,24 @@ import static org.mockito.Mockito.when;
  */
 public class GraphFeatureSetTests extends ESTestCase {
 
-    private GraphLicensee licensee;
+    private XPackLicenseState licenseState;
     private NamedWriteableRegistry namedWriteableRegistry;
 
     @Before
     public void init() throws Exception {
-        licensee = mock(GraphLicensee.class);
+        licenseState = mock(XPackLicenseState.class);
         namedWriteableRegistry = mock(NamedWriteableRegistry.class);
     }
 
     public void testWritableRegistration() throws Exception {
-        new GraphFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry);
+        new GraphFeatureSet(Settings.EMPTY, licenseState, namedWriteableRegistry);
         verify(namedWriteableRegistry).register(eq(GraphFeatureSet.Usage.class), eq("xpack.usage.graph"), anyObject());
     }
 
     public void testAvailable() throws Exception {
-        GraphFeatureSet featureSet = new GraphFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry);
+        GraphFeatureSet featureSet = new GraphFeatureSet(Settings.EMPTY, licenseState, namedWriteableRegistry);
         boolean available = randomBoolean();
-        when(licensee.isAvailable()).thenReturn(available);
+        when(licenseState.isGraphAllowed()).thenReturn(available);
         assertThat(featureSet.available(), is(available));
     }
 
@@ -55,7 +54,7 @@ public class GraphFeatureSetTests extends ESTestCase {
         } else {
             settings.put("xpack.graph.enabled", enabled);
         }
-        GraphFeatureSet featureSet = new GraphFeatureSet(settings.build(), licensee, namedWriteableRegistry);
+        GraphFeatureSet featureSet = new GraphFeatureSet(settings.build(), licenseState, namedWriteableRegistry);
         assertThat(featureSet.enabled(), is(enabled));
     }
 
diff --git a/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/license/LicenseTests.java b/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/license/LicenseTests.java
deleted file mode 100644
index d68c5195402..00000000000
--- a/elasticsearch/x-pack/graph/src/test/java/org/elasticsearch/xpack/graph/license/LicenseTests.java
+++ /dev/null
@@ -1,102 +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.xpack.graph.license;
-
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeTestCase;
-import org.elasticsearch.xpack.graph.GraphLicensee;
-
-import static org.hamcrest.Matchers.is;
-
-public class LicenseTests extends AbstractLicenseeTestCase {
-
-    GraphLicensee graphLicensee = new GraphLicensee(Settings.EMPTY);
-
-    public void testPlatinumTrialLicenseCanDoEverything() throws Exception {
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-    }
-
-    public void testBasicLicenseIsDisabled() throws Exception {
-        setOperationMode(graphLicensee, OperationMode.BASIC);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }
-    
-    public void testStandardLicenseIsDisabled() throws Exception {
-        setOperationMode(graphLicensee, OperationMode.STANDARD);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }    
-
-    public void testNoLicenseDoesNotWork() {
-        setOperationMode(graphLicensee, OperationMode.BASIC);
-        disable(graphLicensee);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }
-
-    public void testExpiredPlatinumTrialLicenseIsRestricted() throws Exception {
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        disable(graphLicensee);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }
-
-    public void testUpgradingFromBasicLicenseWorks() {
-        setOperationMode(graphLicensee, OperationMode.BASIC);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-    }
-
-    public void testDowngradingToBasicLicenseWorks() {
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-
-        setOperationMode(graphLicensee, OperationMode.BASIC);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }
-    
-    public void testUpgradingFromStandardLicenseWorks() {
-        setOperationMode(graphLicensee, OperationMode.STANDARD);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-    }
-
-    public void testDowngradingToStandardLicenseWorks() {
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-
-        setOperationMode(graphLicensee, OperationMode.STANDARD);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }    
-    
-    public void testDowngradingToGoldLicenseWorks() {
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-
-        setOperationMode(graphLicensee, OperationMode.GOLD);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-    }    
-
-    public void testUpgradingExpiredLicenseWorks() {
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        disable(graphLicensee);
-        assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(graphLicensee);
-
-        setOperationMode(graphLicensee, randomTrialOrPlatinumMode());
-        assertLicensePlatinumTrialBehaviour(graphLicensee);
-    }
-
-    private void assertLicensePlatinumTrialBehaviour(GraphLicensee graphLicensee) {
-        assertThat("Expected graph exploration to be allowed", graphLicensee.isAvailable(), is(true));
-    }
-
-    private void assertLicenseBasicOrStandardGoldOrNoneOrExpiredBehaviour(GraphLicensee graphLicensee) {
-        assertThat("Expected graph exploration not to be allowed", graphLicensee.isAvailable(), is(false));
-    }
-}
diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java
index 8fcc604d28f..aeaca77fbeb 100644
--- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java
+++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java
@@ -5,6 +5,11 @@
  */
 package org.elasticsearch.license.plugin;
 
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
 import org.elasticsearch.action.ActionRequest;
 import org.elasticsearch.action.ActionResponse;
 import org.elasticsearch.cluster.metadata.MetaData;
@@ -21,23 +26,14 @@ import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
 import org.elasticsearch.license.plugin.action.put.TransportPutLicenseAction;
 import org.elasticsearch.license.plugin.core.LicensesMetaData;
 import org.elasticsearch.license.plugin.core.LicenseService;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.license.plugin.rest.RestDeleteLicenseAction;
 import org.elasticsearch.license.plugin.rest.RestGetLicenseAction;
 import org.elasticsearch.license.plugin.rest.RestPutLicenseAction;
 import org.elasticsearch.plugins.ActionPlugin;
 import org.elasticsearch.rest.RestHandler;
 import org.elasticsearch.watcher.ResourceWatcherService;
-import org.elasticsearch.xpack.graph.GraphLicensee;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
-import org.elasticsearch.xpack.security.SecurityLicensee;
 import org.elasticsearch.xpack.support.clock.Clock;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
 
 import static java.util.Collections.emptyList;
 import static org.elasticsearch.xpack.XPackPlugin.isTribeNode;
@@ -87,16 +83,10 @@ public class Licensing implements ActionPlugin {
 
     public Collection<Object> createComponents(ClusterService clusterService, Clock clock, Environment environment,
                                                ResourceWatcherService resourceWatcherService,
-                                               SecurityLicenseState securityLicenseState) {
-        SecurityLicensee securityLicensee = new SecurityLicensee(settings, securityLicenseState);
-        WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
-        MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
-        GraphLicensee graphLicensee = new GraphLicensee(settings);
+                                               XPackLicenseState licenseState) {
         LicenseService licenseService = new LicenseService(settings, clusterService, clock,
-                environment, resourceWatcherService,
-                Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee));
-
-        return Arrays.asList(licenseService, securityLicenseState, securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee);
+                environment, resourceWatcherService, licenseState);
+        return Arrays.asList(licenseService, licenseState);
     }
 
     public List<Setting<?>> getSettings() {
diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/AbstractLicenseeComponent.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/AbstractLicenseeComponent.java
deleted file mode 100644
index 5857f603ef8..00000000000
--- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/AbstractLicenseeComponent.java
+++ /dev/null
@@ -1,60 +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.component.AbstractComponent;
-import org.elasticsearch.common.component.AbstractLifecycleComponent;
-import org.elasticsearch.common.settings.Settings;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * A supporting base class for injectable Licensee components.
- */
-public abstract class AbstractLicenseeComponent extends AbstractComponent implements Licensee {
-
-    private final String id;
-    private final List<Listener> listeners = new CopyOnWriteArrayList<>();
-
-    // we initialize the licensee state to enabled with trial operation mode
-    protected volatile Status status = Status.ENABLED;
-
-    protected AbstractLicenseeComponent(Settings settings, String id) {
-        super(settings);
-        this.id = id;
-    }
-
-    @Override
-    public final String id() {
-        return id;
-    }
-
-    /**
-     * @return the current status of this licensee (can never be null)
-     */
-    public Status getStatus() {
-        return status;
-    }
-
-    public void add(Listener listener) {
-        listeners.add(listener);
-    }
-
-    @Override
-    public void onChange(Status status) {
-        this.status = status;
-        logger.trace("[{}] is running in [{}] mode", id(), status);
-        for (Listener listener : listeners) {
-            listener.onChange(status);
-        }
-    }
-
-    public interface Listener {
-        void onChange(Status status);
-    }
-
-}
diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseService.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseService.java
index 5f36131bac6..322bd925537 100644
--- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseService.java
+++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseService.java
@@ -44,18 +44,13 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
 
 /**
- * Service responsible for managing {@link LicensesMetaData}
- * Interfaces through which this is exposed are:
- * - LicensesClientService - responsible for listener registration of consumer plugin(s)
+ * Service responsible for managing {@link LicensesMetaData}.
  * <p>
- * Notification Scheme:
- * <p>
- * All registered listeners are notified of the current license upon registration or when a new license is installed in the cluster state.
- * When a new license is notified as enabled to the registered listener, a notification is scheduled at the time of license expiry.
- * Registered listeners are notified using {@link #onUpdate(LicensesMetaData)}
+ * On the master node, the service handles updating the cluster state when a new license is registered.
+ * It also listens on all nodes for cluster state updates, and updates {@link XPackLicenseState} when
+ * the license changes are detected in the cluster state.
  */
 public class LicenseService extends AbstractLifecycleComponent implements ClusterStateListener, SchedulerEngine.Listener {
 
@@ -65,14 +60,12 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
     /**
      * Duration of grace period after a license has expired
      */
-    public static final TimeValue GRACE_PERIOD_DURATION = days(7);
+    static final TimeValue GRACE_PERIOD_DURATION = days(7);
 
     private final ClusterService clusterService;
 
-    /**
-     * Currently active consumers to notify to
-     */
-    private final List<InternalLicensee> registeredLicensees;
+    /** The xpack feature state to update when license changes are made. */
+    private final XPackLicenseState licenseState;
 
     /**
      * Currently active license
@@ -104,115 +97,70 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
             "please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:";
 
     public LicenseService(Settings settings, ClusterService clusterService, Clock clock, Environment env,
-                          ResourceWatcherService resourceWatcherService, List<Licensee> registeredLicensees) {
+                          ResourceWatcherService resourceWatcherService, XPackLicenseState licenseState) {
         super(settings);
         this.clusterService = clusterService;
         this.clock = clock;
         this.scheduler = new SchedulerEngine(clock);
-        this.registeredLicensees = registeredLicensees.stream().map(InternalLicensee::new).collect(Collectors.toList());
+        this.licenseState = licenseState;
         this.operationModeFileWatcher = new OperationModeFileWatcher(resourceWatcherService,
-                XPackPlugin.resolveConfigFile(env, "license_mode"), logger, () -> notifyLicensees(getLicense()));
+                XPackPlugin.resolveConfigFile(env, "license_mode"), logger, () -> updateLicenseState(getLicense()));
         this.scheduler.register(this);
         populateExpirationCallbacks();
     }
 
+    private void logExpirationWarning(long expirationMillis, boolean expired) {
+        String expiredMsg = expired ? "will expire" : "expired";
+        String general = LoggerMessageFormat.format(null, "\n" +
+            "#\n" +
+            "# License [{}] on [{}]. If you have a new license, please update it.\n" +
+            "# Otherwise, please reach out to your support contact.\n" +
+            "# ", expiredMsg, DATE_FORMATTER.printer().print(expirationMillis));
+        if (expired) {
+            general = general.toUpperCase(Locale.ROOT);
+        }
+        StringBuilder builder = new StringBuilder(general);
+        builder.append(System.lineSeparator());
+        if (expired) {
+            builder.append("# COMMERCIAL PLUGINS OPERATING WITH REDUCED FUNCTIONALITY");
+        } else {
+            builder.append("# Commercial plugins operate with reduced functionality on license expiration:");
+        }
+        XPackLicenseState.EXPIRATION_MESSAGES.forEach((feature, messages) -> {
+            if (messages.length > 0) {
+                builder.append(System.lineSeparator());
+                builder.append("# - ");
+                builder.append(feature);
+                for (String message : messages) {
+                    builder.append(System.lineSeparator());
+                    builder.append("#  - ");
+                    builder.append(message);
+                }
+            }
+        });
+        logger.warn("{}", builder);
+    }
+
     private void populateExpirationCallbacks() {
         expirationCallbacks.add(new ExpirationCallback.Pre(days(7), days(25), days(1)) {
-                                    @Override
-                                    public void on(License license) {
-                                        String general = LoggerMessageFormat.format(null, "\n" +
-                                                "#\n" +
-                                                "# License will expire on [{}]. If you have a new license, please update it.\n" +
-                                                "# Otherwise, please reach out to your support contact.\n" +
-                                                "# ", DATE_FORMATTER.printer().print(license.expiryDate()));
-                                        if (!registeredLicensees.isEmpty()) {
-                                            StringBuilder builder = new StringBuilder(general);
-                                            builder.append(System.lineSeparator());
-                                            builder.append("# Commercial plugins operate with reduced functionality on license " +
-                                                    "expiration:");
-                                            for (InternalLicensee licensee : registeredLicensees) {
-                                                if (licensee.expirationMessages().length > 0) {
-                                                    builder.append(System.lineSeparator());
-                                                    builder.append("# - ");
-                                                    builder.append(licensee.id());
-                                                    for (String message : licensee.expirationMessages()) {
-                                                        builder.append(System.lineSeparator());
-                                                        builder.append("#  - ");
-                                                        builder.append(message);
-                                                    }
-                                                }
-                                            }
-                                            logger.warn("{}", builder);
-                                        } else {
-                                            logger.warn("{}", general);
-                                        }
-                                    }
-                                }
-        );
+            @Override
+            public void on(License license) {
+                logExpirationWarning(license.expiryDate(), false);
+            }
+        });
         expirationCallbacks.add(new ExpirationCallback.Pre(days(0), days(7), TimeValue.timeValueMinutes(10)) {
-                                    @Override
-                                    public void on(License license) {
-                                        String general = LoggerMessageFormat.format(null, "\n" +
-                                                "#\n" +
-                                                "# License will expire on [{}]. If you have a new license, please update it.\n" +
-                                                "# Otherwise, please reach out to your support contact.\n" +
-                                                "# ", DATE_FORMATTER.printer().print(license.expiryDate()));
-                                        if (!registeredLicensees.isEmpty()) {
-                                            StringBuilder builder = new StringBuilder(general);
-                                            builder.append(System.lineSeparator());
-                                            builder.append("# Commercial plugins operate with reduced functionality on license " +
-                                                    "expiration:");
-                                            for (InternalLicensee licensee : registeredLicensees) {
-                                                if (licensee.expirationMessages().length > 0) {
-                                                    builder.append(System.lineSeparator());
-                                                    builder.append("# - ");
-                                                    builder.append(licensee.id());
-                                                    for (String message : licensee.expirationMessages()) {
-                                                        builder.append(System.lineSeparator());
-                                                        builder.append("#  - ");
-                                                        builder.append(message);
-                                                    }
-                                                }
-                                            }
-                                            logger.warn("{}", builder.toString());
-                                        } else {
-                                            logger.warn("{}", general);
-                                        }
-                                    }
-                                }
-        );
+            @Override
+            public void on(License license) {
+                logExpirationWarning(license.expiryDate(), false);
+            }
+        });
         expirationCallbacks.add(new ExpirationCallback.Post(days(0), null, TimeValue.timeValueMinutes(10)) {
-                                    @Override
-                                    public void on(License license) {
-                                        // logged when grace period begins
-                                        String general = LoggerMessageFormat.format(null, "\n" +
-                                                "#\n" +
-                                                "# LICENSE EXPIRED ON [{}]. IF YOU HAVE A NEW LICENSE, PLEASE\n" +
-                                                "# UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
-                                                "# ", DATE_FORMATTER.printer().print(license.expiryDate()));
-                                        if (!registeredLicensees.isEmpty()) {
-                                            StringBuilder builder = new StringBuilder(general);
-                                            builder.append(System.lineSeparator());
-                                            builder.append("# COMMERCIAL PLUGINS OPERATING WITH REDUCED FUNCTIONALITY");
-                                            for (InternalLicensee licensee : registeredLicensees) {
-                                                if (licensee.expirationMessages().length > 0) {
-                                                    builder.append(System.lineSeparator());
-                                                    builder.append("# - ");
-                                                    builder.append(licensee.id());
-                                                    for (String message : licensee.expirationMessages()) {
-                                                        builder.append(System.lineSeparator());
-                                                        builder.append("#  - ");
-                                                        builder.append(message);
-                                                    }
-                                                }
-                                            }
-                                            logger.warn("{}", builder.toString());
-                                        } else {
-                                            logger.warn("{}", general);
-                                        }
-                                    }
-                                }
-        );
+            @Override
+            public void on(License license) {
+                // logged when grace period begins
+                logExpirationWarning(license.expiryDate(), true);
+            }
+        });
     }
 
     /**
@@ -228,23 +176,23 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
             listener.onResponse(new PutLicenseResponse(true, LicensesStatus.EXPIRED));
         } else {
             if (!request.acknowledged()) {
+                // TODO: ack messages should be generated on the master, since another node's cluster state may be behind...
                 final License currentLicense = getLicense();
                 if (currentLicense != null) {
-                    Map<String, String[]> acknowledgeMessages = new HashMap<>(registeredLicensees.size() + 1);
+                    Map<String, String[]> acknowledgeMessages = new HashMap<>();
                     if (!License.isAutoGeneratedLicense(currentLicense.signature()) // current license is not auto-generated
                             && currentLicense.issueDate() > newLicense.issueDate()) { // and has a later issue date
-                        acknowledgeMessages.put("license",
-                                new String[]{"The new license is older than the currently installed license. Are you sure you want to " +
-                                        "override the current license?"});
+                        acknowledgeMessages.put("license", new String[]{
+                            "The new license is older than the currently installed license. " +
+                            "Are you sure you want to override the current license?"});
                     }
-                    for (InternalLicensee licensee : registeredLicensees) {
-                        String[] listenerAcknowledgeMessages = licensee.acknowledgmentMessages(
-                                currentLicense.operationMode(), newLicense.operationMode());
-                        if (listenerAcknowledgeMessages.length > 0) {
-                            acknowledgeMessages.put(licensee.id(), listenerAcknowledgeMessages);
+                    XPackLicenseState.ACKNOWLEDGMENT_MESSAGES.forEach((feature, ackMessages) -> {
+                        String[] messages = ackMessages.apply(currentLicense.operationMode(), newLicense.operationMode());
+                        if (messages.length > 0) {
+                            acknowledgeMessages.put(feature, messages);
                         }
-                    }
-                    if (!acknowledgeMessages.isEmpty()) {
+                    });
+                    if (acknowledgeMessages.isEmpty() == false) {
                         // needs acknowledgement
                         listener.onResponse(new PutLicenseResponse(false, LicensesStatus.VALID, ACKNOWLEDGEMENT_HEADER,
                                 acknowledgeMessages));
@@ -280,7 +228,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
         if (licensesMetaData != null) {
             final License license = licensesMetaData.getLicense();
             if (event.getJobName().equals(LICENSE_JOB)) {
-                notifyLicensees(license);
+                updateLicenseState(license);
             } else if (event.getJobName().startsWith(ExpirationCallback.EXPIRATION_JOB_PREFIX)) {
                 expirationCallbacks.stream()
                         .filter(expirationCallback -> expirationCallback.getId().equals(event.getJobName()))
@@ -315,17 +263,6 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
                 });
     }
 
-    public Licensee.Status licenseeStatus(License license) {
-        if (license == null) {
-            return new Licensee.Status(License.OperationMode.MISSING, false);
-        }
-        long time = clock.millis();
-        boolean active = time >= license.issueDate() &&
-            time < license.expiryDate() + GRACE_PERIOD_DURATION.getMillis();
-
-        return new Licensee.Status(license.operationMode(), active);
-    }
-
     public License getLicense() {
         final License license = getLicense(clusterService.state().metaData().custom(LicensesMetaData.TYPE));
         return license == LicensesMetaData.LICENSE_TOMBSTONE ? null : license;
@@ -379,15 +316,25 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
     protected void doStart() throws ElasticsearchException {
         clusterService.add(this);
         scheduler.start(Collections.emptyList());
-        registeredLicensees.forEach(x -> initLicensee(x.licensee));
+        logger.debug("initializing license state");
+        final ClusterState clusterState = clusterService.state();
+        if (clusterService.lifecycleState() == Lifecycle.State.STARTED
+            && clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK) == false
+            && clusterState.nodes().getMasterNode() != null) {
+            final LicensesMetaData currentMetaData = clusterState.metaData().custom(LicensesMetaData.TYPE);
+            if (clusterState.getNodes().isLocalNodeElectedMaster() &&
+                (currentMetaData == null || currentMetaData.getLicense() == null)) {
+                // triggers a cluster changed event
+                // eventually notifying the current licensee
+                registerTrialLicense();
+            }
+        }
     }
 
     @Override
     protected void doStop() throws ElasticsearchException {
         clusterService.remove(this);
         scheduler.stop();
-        // clear all handlers
-        registeredLicensees.clear();
         // clear current license
         currentLicense.set(null);
     }
@@ -432,23 +379,18 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
         }
     }
 
-    private void notifyLicensees(final License license) {
+    protected void updateLicenseState(final License license) {
         if (license == LicensesMetaData.LICENSE_TOMBSTONE) {
             // implies license has been explicitly deleted
-            // update licensee states
-            registeredLicensees.forEach(InternalLicensee::onRemove);
+            licenseState.update(License.OperationMode.MISSING, false);
             return;
         }
         if (license != null) {
-            logger.debug("notifying [{}] listeners", registeredLicensees.size());
             long time = clock.millis();
             boolean active = time >= license.issueDate() &&
                 time < license.expiryDate() + GRACE_PERIOD_DURATION.getMillis();
+            licenseState.update(license.operationMode(), active);
 
-            Licensee.Status status = new Licensee.Status(license.operationMode(), active);
-            for (InternalLicensee licensee : registeredLicensees) {
-                licensee.onChange(status);
-            }
             if (active) {
                 if (time < license.expiryDate()) {
                     logger.debug("license [{}] - valid", license.uid());
@@ -487,7 +429,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
                     previousLicense.removeOperationModeFileWatcher();
                 }
             }
-            notifyLicensees(license);
+            updateLicenseState(license);
         }
     }
 
@@ -510,24 +452,6 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
         };
     }
 
-    private void initLicensee(Licensee licensee) {
-        logger.debug("initializing licensee [{}]", licensee.id());
-        final ClusterState clusterState = clusterService.state();
-        if (clusterService.lifecycleState() == Lifecycle.State.STARTED
-                && clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK) == false
-                && clusterState.nodes().getMasterNode() != null) {
-            final LicensesMetaData currentMetaData = clusterState.metaData().custom(LicensesMetaData.TYPE);
-            if (clusterState.getNodes().isLocalNodeElectedMaster() &&
-                (currentMetaData == null || currentMetaData.getLicense() == null)) {
-                // triggers a cluster changed event
-                // eventually notifying the current licensee
-                registerTrialLicense();
-            } else {
-                notifyLicensees(currentMetaData.getLicense());
-            }
-        }
-    }
-
     License getLicense(final LicensesMetaData metaData) {
         if (metaData != null) {
             License license = metaData.getLicense();
@@ -543,48 +467,4 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
         }
         return null;
     }
-
-    /**
-     * Stores acknowledgement, expiration and license notification callbacks
-     * for a registered listener
-     */
-    private final class InternalLicensee {
-        volatile Licensee.Status currentStatus = Licensee.Status.MISSING;
-
-        private final Licensee licensee;
-
-        private InternalLicensee(Licensee licensee) {
-            this.licensee = licensee;
-        }
-
-        @Override
-        public String toString() {
-            return "(listener: " + licensee.id() + ", state: " + currentStatus + ")";
-        }
-
-        public String id() {
-            return licensee.id();
-        }
-
-        public String[] expirationMessages() {
-            return licensee.expirationMessages();
-        }
-
-        public String[] acknowledgmentMessages(License.OperationMode currentMode, License.OperationMode newMode) {
-            return licensee.acknowledgmentMessages(currentMode, newMode);
-        }
-
-        public synchronized void onChange(final Licensee.Status status) {
-            if (currentStatus == null // not yet initialized
-                    || !currentStatus.equals(status)) {  // current license has changed
-                logger.debug("licensee [{}] notified", licensee.id());
-                licensee.onChange(status);
-                currentStatus = status;
-            }
-        }
-
-        public void onRemove() {
-            onChange(Licensee.Status.MISSING);
-        }
-    }
 }
\ No newline at end of file
diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java
deleted file mode 100644
index 1fcc12e6976..00000000000
--- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java
+++ /dev/null
@@ -1,104 +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.license.core.License.OperationMode;
-
-import java.util.Locale;
-import java.util.Objects;
-
-public interface Licensee {
-
-    /**
-     * Unique id used to log expiry and
-     * acknowledgment messages
-     */
-    String id();
-
-    /**
-     * Messages to be printed when
-     * logging license expiry warnings
-     */
-    String[] expirationMessages();
-
-    /**
-     * Messages to be returned when
-     * changing from current operation mode
-     * to new operation mode
-     */
-    String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode);
-
-    /**
-     * Notifies when a new license is activated
-     * or when a license state change has occurred
-     */
-    void onChange(Status status);
-
-    /**
-     * {@code Status} represents both the type and state of a license.
-     * <p>
-     * Most places in the code are expected to use {@code volatile} {@code Status} fields. It's important to follow use a local reference
-     * whenever checking different parts of the {@code Status}:
-     * <pre>
-     * Status status = this.status;
-     * return status.isActive() &amp;&amp;
-     *        (status.getMode() == OperationMode.TRIAL || status.getMode == OperationMode.PLATINUM);
-     * </pre>
-     * Otherwise the license has the potential to change in-between both checks.
-     */
-    class Status {
-
-        public static Status ENABLED = new Status(OperationMode.TRIAL, true);
-        public static Status MISSING = new Status(OperationMode.MISSING, false);
-
-        private final OperationMode mode;
-        private final boolean active;
-
-        public Status(OperationMode mode, boolean active) {
-            this.mode = mode;
-            this.active = active;
-        }
-
-        /**
-         * Returns the operation mode of the license
-         * responsible for the current <code>licenseState</code>
-         * <p>
-         * Note: Knowing the mode does not indicate whether the license is active. If that matters (e.g.,
-         * disabling services when a license becomes disabled), then check {@link #isActive()}.
-         */
-        public OperationMode getMode() {
-            return mode;
-        }
-
-        /** Returns true if the license is within the issue date and grace period, or false otherwise */
-        public boolean isActive() {
-            return active;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            Status status = (Status) o;
-            return active == status.active &&
-                mode == status.mode;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mode, active);
-        }
-
-        @Override
-        public String toString() {
-            if (active) {
-                return mode.name().toLowerCase(Locale.ROOT);
-            } else {
-                return "disabled " + mode.name().toLowerCase(Locale.ROOT);
-            }
-        }
-    }
-}
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicenseTribeTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicenseTribeTests.java
index 29d0f7766b5..663c57b8166 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicenseTribeTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicenseTribeTests.java
@@ -15,7 +15,7 @@ import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
 import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
 import org.elasticsearch.xpack.TribeTransportTestCase;
 
-import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
+import static org.elasticsearch.license.plugin.core.TestUtils.generateSignedLicense;
 
 public class LicenseTribeTests extends TribeTransportTestCase {
 
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java
index a184dc50570..0c83cfc45a4 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesTransportTests.java
@@ -19,6 +19,7 @@ import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
 import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
 import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
 import org.elasticsearch.license.plugin.core.LicensesStatus;
+import org.elasticsearch.license.plugin.core.TestUtils;
 import org.elasticsearch.xpack.monitoring.Monitoring;
 import org.elasticsearch.node.Node;
 import org.elasticsearch.plugins.Plugin;
@@ -30,9 +31,8 @@ import org.elasticsearch.xpack.watcher.Watcher;
 import java.util.Collection;
 import java.util.Collections;
 
-import static org.elasticsearch.license.plugin.TestUtils.dateMath;
-import static org.elasticsearch.license.plugin.TestUtils.generateExpiredLicense;
-import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
+import static org.elasticsearch.license.plugin.core.TestUtils.generateExpiredLicense;
+import static org.elasticsearch.license.plugin.core.TestUtils.generateSignedLicense;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java
index fc2977c8ff0..4b524c85c85 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java
@@ -5,8 +5,6 @@
  */
 package org.elasticsearch.license.plugin.core;
 
-import java.util.Arrays;
-
 import org.elasticsearch.Version;
 import org.elasticsearch.cluster.ClusterName;
 import org.elasticsearch.cluster.ClusterState;
@@ -51,11 +49,11 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
         environment = mock(Environment.class);
     }
 
-    protected void setInitialState(License license, Licensee... licensees) {
+    protected void setInitialState(License license, XPackLicenseState licenseState) {
         Path tempDir = createTempDir();
         when(environment.configFile()).thenReturn(tempDir);
         licenseService = new LicenseService(Settings.EMPTY, clusterService, clock, environment,
-                resourceWatcherService, Arrays.asList(licensees));
+                resourceWatcherService, licenseState);
         ClusterState state = mock(ClusterState.class);
         final ClusterBlocks noBlock = ClusterBlocks.builder().build();
         when(state.blocks()).thenReturn(noBlock);
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java
deleted file mode 100644
index 7390fa8c40a..00000000000
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java
+++ /dev/null
@@ -1,163 +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.component.AbstractComponent;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.test.ESTestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * Provides helper methods for {@link Licensee} implementations.
- * <p>
- * Note: This requires that this class be on the classpath for those implementations.
- */
-public abstract class AbstractLicenseeTestCase extends ESTestCase {
-    /**
-     * Ensure when going from {@code fromMode} to {@code toMode}, nothing gets reported.
-     * <p>
-     * This will randomly {@code null}-out the {@code fromMode} license.
-     *
-     * @param fromMode Original / current license
-     * @param toMode New license
-     * @param licensee The licensee to test
-     */
-    public static void assertEmptyAck(OperationMode fromMode, OperationMode toMode, Licensee licensee) {
-        // test it
-        String[] messages = licensee.acknowledgmentMessages(fromMode, toMode);
-
-        assertThat(fromToMessage(fromMode, toMode), messages.length, equalTo(0));
-    }
-
-    /**
-     * Ensure when going from {@code fromMode} to {@code toMode}, nothing gets reported.
-     * <p>
-     * This will randomly {@code null}-out the {@code fromMode} license.
-     *
-     * @param fromMode Original / current license
-     * @param toMode New license
-     * @param licenseeSupplier Supplies the licensee to test
-     */
-    public static void assertEmptyAck(OperationMode fromMode, OperationMode toMode, Supplier<Licensee> licenseeSupplier) {
-        assertEmptyAck(fromMode, toMode, licenseeSupplier.get());
-    }
-
-    /**
-     * Get the ack when changing {@code fromMode} to {@code toMode}.
-     * <p>
-     * This just serves to remove a lot of duplicated code.
-     *
-     * @param fromMode Original / current license
-     * @param toMode New license
-     * @param licensee The licensee to test
-     */
-    public static String[] ackLicenseChange(OperationMode fromMode, OperationMode toMode, Licensee licensee) {
-        return licensee.acknowledgmentMessages(fromMode, toMode);
-    }
-
-    /**
-     * Ensure when going from {@code fromMode} to {@code toMode}, nothing gets reported.
-     * <p>
-     * This just serves to remove a lot of duplicated code.
-     *
-     * @param fromMode Original / current license
-     * @param toMode New license
-     * @param licenseeSupplier Supplies the licensee to test
-     */
-    public static String[] ackLicenseChange(OperationMode fromMode, OperationMode toMode, Supplier<Licensee> licenseeSupplier) {
-        return ackLicenseChange(fromMode, toMode, licenseeSupplier.get());
-    }
-
-    /**
-     * Randomly get {@link OperationMode#TRIAL} or {@link OperationMode#PLATINUM}.
-     *
-     * @return Never {@code null}.
-     */
-    public static OperationMode randomTrialOrPlatinumMode() {
-        return randomFrom(OperationMode.TRIAL, OperationMode.PLATINUM);
-    }
-
-    /**
-     * Randomly get {@link OperationMode#TRIAL}, {@link OperationMode#STANDARD}, {@link OperationMode#GOLD}, or
-     * {@link OperationMode#PLATINUM}.
-     *
-     * @return Never {@code null}.
-     */
-    public static OperationMode randomTrialStandardGoldOrPlatinumMode() {
-        return randomFrom(OperationMode.TRIAL, OperationMode.STANDARD, OperationMode.GOLD, OperationMode.PLATINUM);
-    }
-
-    /**
-     * Randomly get any {@link OperationMode}.
-     *
-     * @return Never {@code null}.
-     */
-    public static OperationMode randomMode() {
-        return randomFrom(OperationMode.values());
-    }
-
-    /**
-     * Get any {@link #randomMode() mode}, except the selected {@code mode}.
-     *
-     * @param mode The mode to exclude.
-     * @return Never {@code null}.
-     */
-    public static OperationMode randomModeExcept(OperationMode mode) {
-        return randomValueOtherThan(mode, AbstractLicenseeTestCase::randomMode);
-    }
-
-    /**
-     * Get a random value from the {@code values} that passes {@code filter}.
-     *
-     * @param values The values to filter and randomly select from
-     * @param filter The filter to apply
-     * @return Never {@code null}.
-     * @throws IllegalArgumentException if nothing matches the {@code filter}
-     * @see #randomFrom(Object[])
-     */
-    public static <T> T randomFrom(T[] values, Predicate<T> filter) {
-        return randomFrom(Arrays.stream(values).filter(filter).collect(Collectors.toList()));
-    }
-
-    /**
-     * Get a message to show with assertions for license transition.
-     *
-     * @param fromMode Coming "from" mode
-     * @param toMode Going "to" mode
-     * @return Never {@code null}.
-     */
-    public static String fromToMessage(OperationMode fromMode, OperationMode toMode) {
-        return String.format(Locale.ROOT, "From [%s] to [%s]", fromMode, toMode);
-    }
-
-    private OperationMode operationMode;
-
-    public void setOperationMode(Licensee licensee, OperationMode operationMode) {
-        this.operationMode = operationMode;
-        enable(licensee);
-    }
-
-    public void disable(Licensee licensee) {
-        licensee.onChange(new Licensee.Status(operationMode, false));
-    }
-
-    public void enable(Licensee licensee) {
-        licensee.onChange(new Licensee.Status(operationMode, true));
-    }
-}
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java
index 6af2c7ad787..1b4e6aac870 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseClusterChangeTests.java
@@ -16,7 +16,6 @@ import org.elasticsearch.cluster.node.DiscoveryNodes;
 import org.elasticsearch.common.transport.LocalTransportAddress;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.TestUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
@@ -31,12 +30,12 @@ import static org.mockito.Mockito.when;
 
 public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
 
-    private TestUtils.AssertingLicensee licensee;
+    private TestUtils.AssertingLicenseState licenseState;
 
     @Before
     public void setup() {
-        licensee = new TestUtils.AssertingLicensee("LicenseClusterChangeTests", logger);
-        setInitialState(null, licensee);
+        licenseState = new TestUtils.AssertingLicenseState();
+        setInitialState(null, licenseState);
         licenseService.start();
     }
 
@@ -52,8 +51,8 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
         MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build();
         ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build();
         licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
-        assertThat(licensee.statuses.size(), equalTo(1));
-        assertTrue(licensee.statuses.get(0).isActive());
+        assertThat(licenseState.activeUpdates.size(), equalTo(1));
+        assertTrue(licenseState.activeUpdates.get(0));
     }
 
     public void testNoNotificationOnExistingLicense() throws Exception {
@@ -62,7 +61,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
         ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build();
         ClusterState oldState = ClusterState.builder(newState).build();
         licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
-        assertThat(licensee.statuses.size(), equalTo(0));
+        assertThat(licenseState.activeUpdates.size(), equalTo(0));
     }
 
     public void testTrialLicenseGeneration() throws Exception {
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java
index a4c49cf2837..577d2a6df01 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java
@@ -9,7 +9,6 @@ import org.elasticsearch.cluster.ClusterName;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.ClusterStateUpdateTask;
 import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.license.plugin.TestUtils;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
@@ -21,9 +20,8 @@ import static org.mockito.Mockito.when;
 public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
 
     public void testTrialLicenseRequestOnEmptyLicenseState() throws Exception {
-        TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee(
-            "testTrialLicenseRequestOnEmptyLicenseState", logger);
-        setInitialState(null, licensee);
+        XPackLicenseState licenseState = new XPackLicenseState();
+        setInitialState(null, licenseState);
         when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true);
         licenseService.start();
 
@@ -36,13 +34,4 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
         assertNotNull(licenseMetaData.getLicense());
         assertEquals(clock.millis() + LicenseService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
     }
-
-    public void testNotificationOnRegistration() throws Exception {
-        TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee(
-                "testNotificationOnRegistration", logger);
-        setInitialState(TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)), licensee);
-        licenseService.start();
-        assertThat(licensee.statuses.size(), equalTo(1));
-        assertTrue(licensee.statuses.get(0).isActive());
-    }
 }
\ No newline at end of file
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java
index 06b2e41bbee..37113016360 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java
@@ -7,7 +7,6 @@ package org.elasticsearch.license.plugin.core;
 
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.TestUtils;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.scheduler.SchedulerEngine;
 import org.junit.Before;
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseServiceClusterTests.java
similarity index 86%
rename from elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java
rename to elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseServiceClusterTests.java
index 62edd02f934..ffd83337399 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseServiceClusterTests.java
@@ -3,14 +3,15 @@
  * 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;
+package org.elasticsearch.license.plugin.core;
 
 import org.elasticsearch.common.network.NetworkModule;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.env.Environment;
 import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.core.LicenseService;
+import org.elasticsearch.license.plugin.AbstractLicensesIntegrationTestCase;
+import org.elasticsearch.license.plugin.LicensingClient;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
 import org.elasticsearch.xpack.MockNetty3Plugin;
@@ -22,13 +23,13 @@ import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
 
-import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
+import static org.elasticsearch.license.plugin.core.TestUtils.generateSignedLicense;
 import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.nullValue;
 
 @ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0)
-public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTestCase {
+public class LicenseServiceClusterTests extends AbstractLicensesIntegrationTestCase {
 
     @Override
     protected Settings transportClientSettings() {
@@ -123,40 +124,40 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest
         wipeAllLicenses();
         internalCluster().startNode();
         ensureGreen();
-        assertLicenseState(true);
+        assertLicenseActive(true);
         logger.info("--> restart node");
         internalCluster().fullRestart();
         ensureYellow();
         logger.info("--> await node for enabled");
-        assertLicenseState(true);
+        assertLicenseActive(true);
     }
 
     public void testClusterRestartWhileGrace() throws Exception {
         wipeAllLicenses();
         internalCluster().startNode();
-        assertLicenseState(true);
+        assertLicenseActive(true);
         putLicense(TestUtils.generateSignedLicense(TimeValue.timeValueMillis(0)));
         ensureGreen();
-        assertLicenseState(true);
+        assertLicenseActive(true);
         logger.info("--> restart node");
         internalCluster().fullRestart();
         ensureYellow();
         logger.info("--> await node for grace_period");
-        assertLicenseState(true);
+        assertLicenseActive(true);
     }
 
     public void testClusterRestartWhileExpired() throws Exception {
         wipeAllLicenses();
         internalCluster().startNode();
         ensureGreen();
-        assertLicenseState(true);
+        assertLicenseActive(true);
         putLicense(TestUtils.generateExpiredLicense(System.currentTimeMillis() - LicenseService.GRACE_PERIOD_DURATION.getMillis()));
-        assertLicenseState(false);
+        assertLicenseActive(false);
         logger.info("--> restart node");
         internalCluster().fullRestart();
         ensureYellow();
         logger.info("--> await node for disabled");
-        assertLicenseState(false);
+        assertLicenseActive(false);
     }
 
     public void testClusterNotRecovered() throws Exception {
@@ -164,13 +165,13 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest
         internalCluster().startNode(nodeSettingsBuilder(0).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
         logger.info("--> start second master out of two [recovered state]");
         internalCluster().startNode(nodeSettingsBuilder(1).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
-        assertLicenseState(true);
+        assertLicenseActive(true);
     }
 
-    private void assertLicenseState(boolean active) throws InterruptedException {
+    private void assertLicenseActive(boolean active) throws InterruptedException {
         boolean success = awaitBusy(() -> {
-            for (LicenseService service : internalCluster().getDataNodeInstances(LicenseService.class)) {
-                if (service.licenseeStatus(service.getLicense()).isActive() == active) {
+            for (XPackLicenseState licenseState : internalCluster().getDataNodeInstances(XPackLicenseState.class)) {
+                if (licenseState.isActive() == active) {
                     return true;
                 }
             }
@@ -181,8 +182,8 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest
 
     private void assertOperationMode(License.OperationMode operationMode) throws InterruptedException {
         boolean success = awaitBusy(() -> {
-            for (LicenseService service : internalCluster().getDataNodeInstances(LicenseService.class)) {
-                if (service.licenseeStatus(service.getLicense()).getMode() == operationMode) {
+            for (XPackLicenseState licenseState : internalCluster().getDataNodeInstances(XPackLicenseState.class)) {
+                if (licenseState.getOperationMode() == operationMode) {
                     return true;
                 }
             }
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java
index 40cbd87d393..050c0a37eb4 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java
@@ -5,19 +5,14 @@
  */
 package org.elasticsearch.license.plugin.core;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.cluster.ClusterStateUpdateTask;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.TestUtils;
 import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
 import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
 
-import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
+import static org.elasticsearch.license.plugin.core.TestUtils.generateSignedLicense;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
 import static org.mockito.Matchers.any;
@@ -28,93 +23,40 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase
 
     public void testAcknowledgment() throws Exception {
 
-        String id = "testAcknowledgment";
-        String[] acknowledgeMessages = new String[] {"message"};
-        TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee(id, logger);
-        setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licensee);
-        licenseService.start();
-        licensee.setAcknowledgementMessages(acknowledgeMessages);
-        // try installing a signed license
-        License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10));
-        PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
-        // ensure acknowledgement message was part of the response
-        licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID,
-                Collections.singletonMap(id, acknowledgeMessages)));
-        assertThat(licensee.acknowledgementRequested.size(), equalTo(1));
-        assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
-        assertThat(licenseService.getLicense(), not(signedLicense));
-
-        // try installing a signed license with acknowledgement
-        putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true);
-        // ensure license was installed and no acknowledgment message was returned
-        licensee.setAcknowledgementMessages(new String[0]);
-        licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID,
-                Collections.<String, String[]>emptyMap()));
-        verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
-        assertThat(licensee.acknowledgementRequested.size(), equalTo(1));
-        assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
-    }
-
-    public void testAcknowledgementMultipleLicensee() throws Exception {
-        String id1 = "testAcknowledgementMultipleLicensee_1";
-        String[] acknowledgeMessages1 = new String[] {"testAcknowledgementMultipleLicensee_1"};
-        String id2 = "testAcknowledgementMultipleLicensee_2";
-        String[] acknowledgeMessages2 = new String[] {"testAcknowledgementMultipleLicensee_2"};
-        TestUtils.AssertingLicensee licensee1 = new TestUtils.AssertingLicensee(id1, logger);
-        licensee1.setAcknowledgementMessages(acknowledgeMessages1);
-        TestUtils.AssertingLicensee licensee2 = new TestUtils.AssertingLicensee(id2, logger);
-        licensee2.setAcknowledgementMessages(acknowledgeMessages2);
-        setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licensee1, licensee2);
+        XPackLicenseState licenseState = new XPackLicenseState();
+        setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licenseState);
         licenseService.start();
         // try installing a signed license
         License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10));
         PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
         // ensure acknowledgement message was part of the response
-        final HashMap<String, String[]> expectedMessages = new HashMap<>();
-        expectedMessages.put(id1, acknowledgeMessages1);
-        expectedMessages.put(id2, acknowledgeMessages2);
-        licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID,
-                expectedMessages));
-        verify(clusterService, times(0)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
-        assertThat(licensee2.acknowledgementRequested.size(), equalTo(1));
-        assertThat(licensee2.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
-        assertThat(licensee1.acknowledgementRequested.size(), equalTo(1));
-        assertThat(licensee1.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
+        licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, true));
         assertThat(licenseService.getLicense(), not(signedLicense));
 
         // try installing a signed license with acknowledgement
         putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true);
         // ensure license was installed and no acknowledgment message was returned
-        licensee1.setAcknowledgementMessages(new String[0]);
-        licensee2.setAcknowledgementMessages(new String[0]);
-        licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID,
-                Collections.<String, String[]>emptyMap()));
-        verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
+        licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, false));
+        verify(clusterService, times(2)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
     }
 
     private static class AssertingLicensesUpdateResponse implements ActionListener<PutLicenseResponse> {
         private final boolean expectedAcknowledgement;
         private final LicensesStatus expectedStatus;
-        private final Map<String, String[]> expectedAckMessages;
+        private final boolean expectAckMessages;
 
         public AssertingLicensesUpdateResponse(boolean expectedAcknowledgement, LicensesStatus expectedStatus,
-                                               Map<String, String[]> expectedAckMessages) {
+                                               boolean expectAckMessages) {
             this.expectedAcknowledgement = expectedAcknowledgement;
             this.expectedStatus = expectedStatus;
-            this.expectedAckMessages = expectedAckMessages;
+            this.expectAckMessages = expectAckMessages;
         }
 
         @Override
         public void onResponse(PutLicenseResponse licensesUpdateResponse) {
             assertThat(licensesUpdateResponse.isAcknowledged(), equalTo(expectedAcknowledgement));
             assertThat(licensesUpdateResponse.status(), equalTo(expectedStatus));
-            assertThat(licensesUpdateResponse.acknowledgeMessages().size(), equalTo(expectedAckMessages.size()));
-            for (Map.Entry<String, String[]> expectedEntry : expectedAckMessages.entrySet()) {
-                Map<String, String[]> actual = licensesUpdateResponse.acknowledgeMessages();
-                assertThat(actual.containsKey(expectedEntry.getKey()), equalTo(true));
-                String[] actualMessages = actual.get(expectedEntry.getKey());
-                assertThat(actualMessages, equalTo(expectedEntry.getValue()));
-            }
+            assertEquals(licensesUpdateResponse.acknowledgeMessages().isEmpty(), expectAckMessages == false);
         }
 
         @Override
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java
index a63376a05e8..260135b764a 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesManagerServiceTests.java
@@ -16,7 +16,6 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.TestUtils;
 import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.test.ESSingleNodeTestCase;
@@ -26,7 +25,7 @@ import org.elasticsearch.xpack.monitoring.Monitoring;
 import org.elasticsearch.xpack.security.Security;
 import org.elasticsearch.xpack.watcher.Watcher;
 
-import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense;
+import static org.elasticsearch.license.plugin.core.TestUtils.generateSignedLicense;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesMetaDataSerializationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesMetaDataSerializationTests.java
index 13337e5e29a..4748ab9c065 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesMetaDataSerializationTests.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesMetaDataSerializationTests.java
@@ -22,7 +22,6 @@ import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.license.core.License;
 import org.elasticsearch.license.plugin.Licensing;
-import org.elasticsearch.license.plugin.TestUtils;
 import org.elasticsearch.test.ESTestCase;
 
 import java.util.Base64;
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java
deleted file mode 100644
index 6111fe02bde..00000000000
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java
+++ /dev/null
@@ -1,91 +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 java.util.List;
-
-import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.TestUtils;
-import org.elasticsearch.license.plugin.TestUtils.AssertingLicensee;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-
-import static org.hamcrest.Matchers.equalTo;
-
-public class LicensesNotificationTests extends AbstractLicenseServiceTestCase {
-
-    public void testLicenseNotification() throws Exception {
-        final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(48));
-        int nLicensee = randomIntBetween(1, 3);
-        AssertingLicensee[] assertingLicensees = new AssertingLicensee[nLicensee];
-        for (int i = 0; i < assertingLicensees.length; i++) {
-            assertingLicensees[i] = new AssertingLicensee("testLicenseNotification" + i, logger);
-        }
-        setInitialState(license, assertingLicensees);
-        licenseService.start();
-        for (int i = 0; i < assertingLicensees.length; i++) {
-            assertLicenseStates(assertingLicensees[i], true);
-        }
-        clock.fastForward(TimeValue.timeValueMillis(license.expiryDate() - clock.millis()));
-        final LicensesMetaData licensesMetaData = new LicensesMetaData(license);
-        licenseService.onUpdate(licensesMetaData);
-        for (AssertingLicensee assertingLicensee : assertingLicensees) {
-            assertLicenseStates(assertingLicensee, true);
-        }
-        clock.fastForward(TimeValue.timeValueMillis((license.expiryDate() +
-                LicenseService.GRACE_PERIOD_DURATION.getMillis()) - clock.millis()));
-        licenseService.onUpdate(licensesMetaData);
-        for (AssertingLicensee assertingLicensee : assertingLicensees) {
-            assertLicenseStates(assertingLicensee, true, false);
-        }
-        clock.setTime(new DateTime(DateTimeZone.UTC));
-        final License newLicense = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2));
-        clock.fastForward(TimeValue.timeValueHours(1));
-        LicensesMetaData licensesMetaData1 = new LicensesMetaData(newLicense);
-        licenseService.onUpdate(licensesMetaData1);
-        for (AssertingLicensee assertingLicensee : assertingLicensees) {
-            assertLicenseStates(assertingLicensee, true, false, true);
-        }
-    }
-
-    private void assertLicenseStates(AssertingLicensee licensee, boolean... states) {
-        StringBuilder msg = new StringBuilder();
-        msg.append("Actual: ");
-        msg.append(dumpLicensingStates(licensee.statuses));
-        msg.append(" Expected: ");
-        msg.append(dumpLicensingStates(states));
-        assertThat(msg.toString(), licensee.statuses.size(), equalTo(states.length));
-        for (int i = 0; i < states.length; i++) {
-            assertThat(msg.toString(), licensee.statuses.get(i).isActive(), equalTo(states[i]));
-        }
-    }
-
-    private String dumpLicensingStates(List<Licensee.Status> statuses) {
-        return dumpLicensingStates(statuses.toArray(new Licensee.Status[statuses.size()]));
-    }
-
-    private String dumpLicensingStates(Licensee.Status... statuses) {
-        boolean[] states = new boolean[statuses.length];
-        for (int i = 0; i < statuses.length; i++) {
-            states[i] = statuses[i].isActive();
-        }
-        return dumpLicensingStates(states);
-    }
-
-    private String dumpLicensingStates(boolean... states) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[");
-        for (int i = 0; i < states.length; i++) {
-            sb.append(states[i]);
-            if (i != states.length - 1) {
-                sb.append(", ");
-            }
-        }
-        sb.append("]");
-        return sb.toString();
-    }
-}
diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/TestUtils.java
similarity index 82%
rename from elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java
rename to elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/TestUtils.java
index 6ae7b4a70e5..2a1f6350adf 100644
--- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java
+++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/TestUtils.java
@@ -3,7 +3,7 @@
  * 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;
+package org.elasticsearch.license.plugin.core;
 
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.common.collect.Tuple;
@@ -21,12 +21,10 @@ import org.elasticsearch.license.core.License;
 import org.elasticsearch.license.licensor.LicenseSigner;
 import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
 import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
-import org.elasticsearch.license.plugin.core.LicenseService;
-import org.elasticsearch.license.plugin.core.Licensee;
-import org.elasticsearch.license.plugin.core.LicensesStatus;
 import org.junit.Assert;
 
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.Callable;
@@ -171,44 +169,14 @@ public class TestUtils {
         assertThat(status.get(), equalTo(expectedStatus));
     }
 
-    public static class AssertingLicensee implements Licensee {
-        public final ESLogger logger;
-        public final String id;
-        public final List<Licensee.Status> statuses = new CopyOnWriteArrayList<>();
-        public final AtomicInteger expirationMessagesCalled = new AtomicInteger(0);
-        public final List<Tuple<License.OperationMode, License.OperationMode>> acknowledgementRequested = new CopyOnWriteArrayList<>();
-
-        private String[] acknowledgmentMessages = new String[0];
-
-        public AssertingLicensee(String id, ESLogger logger) {
-            this.logger = logger;
-            this.id = id;
-        }
-
-        public void setAcknowledgementMessages(String[] acknowledgementMessages) {
-            this.acknowledgmentMessages = acknowledgementMessages;
-        }
-        @Override
-        public String id() {
-            return id;
-        }
+    public static class AssertingLicenseState extends XPackLicenseState {
+        public final List<License.OperationMode> modeUpdates = new ArrayList<>();
+        public final List<Boolean> activeUpdates = new ArrayList<>();
 
         @Override
-        public String[] expirationMessages() {
-            expirationMessagesCalled.incrementAndGet();
-            return new String[0];
-        }
-
-        @Override
-        public String[] acknowledgmentMessages(License.OperationMode currentMode, License.OperationMode newMode) {
-            acknowledgementRequested.add(new Tuple<>(currentMode, newMode));
-            return acknowledgmentMessages;
-        }
-
-        @Override
-        public void onChange(Status status) {
-            assertNotNull(status);
-            statuses.add(status);
+        void update(License.OperationMode mode, boolean active) {
+            modeUpdates.add(mode);
+            activeUpdates.add(active);
         }
     }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java
index cef76ffd35a..e70dcd67713 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSet.java
@@ -12,6 +12,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.XPackFeatureSet;
 import org.elasticsearch.xpack.monitoring.agent.exporter.Exporter;
 import org.elasticsearch.xpack.monitoring.agent.exporter.Exporters;
@@ -26,14 +27,14 @@ import java.util.Map;
 public class MonitoringFeatureSet implements XPackFeatureSet {
 
     private final boolean enabled;
-    private final MonitoringLicensee licensee;
+    private final XPackLicenseState licenseState;
     private final Exporters exporters;
 
     @Inject
-    public MonitoringFeatureSet(Settings settings, @Nullable MonitoringLicensee licensee, @Nullable Exporters exporters,
+    public MonitoringFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState, @Nullable Exporters exporters,
                                 NamedWriteableRegistry namedWriteableRegistry) {
         this.enabled = MonitoringSettings.ENABLED.get(settings);
-        this.licensee = licensee;
+        this.licenseState = licenseState;
         this.exporters = exporters;
         namedWriteableRegistry.register(Usage.class, Usage.writeableName(Monitoring.NAME), Usage::new);
     }
@@ -50,7 +51,7 @@ public class MonitoringFeatureSet implements XPackFeatureSet {
 
     @Override
     public boolean available() {
-        return licensee != null && licensee.isAvailable();
+        return licenseState != null && licenseState.isMonitoringAllowed();
     }
 
     @Override
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java
deleted file mode 100644
index 5f0dd674eb4..00000000000
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java
+++ /dev/null
@@ -1,116 +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.xpack.monitoring;
-
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.logging.LoggerMessageFormat;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
-
-/**
- * {@code MonitoringLicensee} determines whether certain features of Monitoring are enabled or disabled.
- * <p>
- * Once the license expires, the agent will stop:
- * <ul>
- * <li>Collecting and publishing new metrics.</li>
- * <li>Cleaning up (deleting) older indices.</li>
- * </ul>
- */
-public class MonitoringLicensee extends AbstractLicenseeComponent {
-
-    public MonitoringLicensee(Settings settings) {
-        super(settings, Monitoring.NAME);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @see #collectionEnabled()
-     * @see #cleaningEnabled()
-     */
-    @Override
-    public String[] expirationMessages() {
-        return new String[] {
-                "The agent will stop collecting cluster and indices metrics",
-                "The agent will stop automatically cleaning indices older than [xpack.monitoring.history.duration]",
-        };
-    }
-
-    @Override
-    public String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode) {
-        switch (newMode) {
-            case BASIC:
-                switch (currentMode) {
-                    case TRIAL:
-                    case STANDARD:
-                    case GOLD:
-                    case PLATINUM:
-                        return new String[] {
-                            LoggerMessageFormat.format(
-                                    "Multi-cluster support is disabled for clusters with [{}] license. If you are\n" +
-                                    "running multiple clusters, users won't be able to access the clusters with\n" +
-                                    "[{}] licenses from within a single X-Pack Kibana instance. You will have to deploy a\n" +
-                                    "separate and dedicated X-pack Kibana instance for each [{}] cluster you wish to monitor.",
-                                    newMode, newMode, newMode),
-                            LoggerMessageFormat.format(
-                                    "Automatic index cleanup is locked to {} days for clusters with [{}] license.",
-                                    MonitoringSettings.HISTORY_DURATION.getDefault(Settings.EMPTY).days(), newMode)
-                        };
-                }
-                break;
-        }
-        return Strings.EMPTY_ARRAY;
-    }
-
-    /**
-     * Monitoring is always available as long as there is a valid license
-     *
-     * @return true
-     */
-    public boolean isAvailable() {
-        return status.isActive();
-    }
-
-    /**
-     * Determine if the index cleaning service is enabled.
-     * <p>
-     * Collection is only disabled <em>automatically</em> when the license expires. All modes are valid for collection.
-     * <p>
-     * Collection <em>can</em> be disabled explicitly by the user, although that's generally a temporary solution to unrelated issues
-     * (e.g., initial setup when the monitoring cluster doesn't actually exist).
-     *
-     * @return {@code true} as long as the license is valid. Otherwise {@code false}.
-     */
-    public boolean collectionEnabled() {
-        return status.isActive();
-    }
-
-    /**
-     * Determine if the index cleaning service is enabled.
-     * <p>
-     * Index cleaning is only disabled when the license expires. All modes are valid for cleaning.
-     *
-     * @return {@code true} as long as the license is valid. Otherwise {@code false}.
-     */
-    public boolean cleaningEnabled() {
-        return status.isActive();
-    }
-
-    /**
-     * Determine if the current license allows the retention of indices to be modified.
-     * <p>
-     * Only users with a non-{@link OperationMode#BASIC} license can update the retention period.
-     * <p>
-     * Note: This does not consider the <em>state</em> of the license so that any change is remembered for when they fix their license.
-     *
-     * @return {@code true} if the user is allowed to modify the retention. Otherwise {@code false}.
-     */
-    public boolean allowUpdateRetention() {
-        final OperationMode mode = status.getMode();
-        return mode != OperationMode.BASIC && mode != OperationMode.MISSING;
-    }
-}
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringModule.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringModule.java
index 2f7162bd808..6c7908b82c3 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringModule.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringModule.java
@@ -29,8 +29,6 @@ public class MonitoringModule extends AbstractModule {
             bind(MonitoringSettings.class).asEagerSingleton();
             bind(AgentService.class).asEagerSingleton();
             bind(CleanerService.class).asEagerSingleton();
-        } else if (transportClientMode) {
-            bind(MonitoringLicensee.class).toProvider(Providers.of(null));
         }
     }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollector.java
index 2d8ee861298..fccfc22e57f 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollector.java
@@ -12,8 +12,8 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.component.AbstractLifecycleComponent;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
 
@@ -25,16 +25,16 @@ public abstract class AbstractCollector extends AbstractLifecycleComponent imple
 
     protected final ClusterService clusterService;
     protected final MonitoringSettings monitoringSettings;
-    protected final MonitoringLicensee licensee;
+    protected final XPackLicenseState licenseState;
 
     @Inject
     public AbstractCollector(Settings settings, String name, ClusterService clusterService,
-                             MonitoringSettings monitoringSettings, MonitoringLicensee licensee) {
+                             MonitoringSettings monitoringSettings, XPackLicenseState licenseState) {
         super(settings);
         this.name = name;
         this.clusterService = clusterService;
         this.monitoringSettings = monitoringSettings;
-        this.licensee = licensee;
+        this.licenseState = licenseState;
     }
 
     @Override
@@ -61,7 +61,7 @@ public abstract class AbstractCollector extends AbstractLifecycleComponent imple
      * Indicates if the current collector is allowed to collect data
      */
     protected boolean shouldCollect() {
-        if (!licensee.collectionEnabled()) {
+        if (licenseState.isMonitoringAllowed() == false) {
             logger.trace("collector [{}] can not collect data due to invalid license", name());
             return false;
         }
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollector.java
index 8c3db642714..938a728c6ca 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollector.java
@@ -13,7 +13,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodes;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -38,8 +38,8 @@ public class ClusterStateCollector extends AbstractCollector {
 
     @Inject
     public ClusterStateCollector(Settings settings, ClusterService clusterService,
-                                 MonitoringSettings monitoringSettings, MonitoringLicensee licensee, InternalClient client) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+                                 MonitoringSettings monitoringSettings, XPackLicenseState licenseState, InternalClient client) {
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
         this.client = client;
     }
 
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollector.java
index b9819159c62..4eee2aa63a0 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollector.java
@@ -13,9 +13,9 @@ import org.elasticsearch.cluster.node.DiscoveryNode;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.plugin.core.LicenseUtils;
 import org.elasticsearch.license.plugin.core.LicenseService;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.LicenseUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -45,9 +45,9 @@ public class ClusterStatsCollector extends AbstractCollector {
 
     @Inject
     public ClusterStatsCollector(Settings settings, ClusterService clusterService,
-                                 MonitoringSettings monitoringSettings, MonitoringLicensee licensee, InternalClient client,
+                                 MonitoringSettings monitoringSettings, XPackLicenseState licenseState, InternalClient client,
                                  LicenseService licenseService) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
         this.client = client;
         this.licenseService = licenseService;
     }
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollector.java
index fcf9fd65e66..14b866b17b0 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollector.java
@@ -13,7 +13,7 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexNotFoundException;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -40,8 +40,8 @@ public class IndexRecoveryCollector extends AbstractCollector {
 
     @Inject
     public IndexRecoveryCollector(Settings settings, ClusterService clusterService,
-                                  MonitoringSettings monitoringSettings, MonitoringLicensee licensee, InternalClient client) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+                                  MonitoringSettings monitoringSettings, XPackLicenseState licenseState, InternalClient client) {
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
         this.client = client;
     }
 
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollector.java
index 0e76ed66a04..b2c2821ff59 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollector.java
@@ -15,7 +15,7 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexNotFoundException;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -42,8 +42,8 @@ public class IndexStatsCollector extends AbstractCollector {
 
     @Inject
     public IndexStatsCollector(Settings settings, ClusterService clusterService,
-                               MonitoringSettings monitoringSettings, MonitoringLicensee licensee, InternalClient client) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+                               MonitoringSettings monitoringSettings, XPackLicenseState licenseState, InternalClient client) {
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
         this.client = client;
     }
 
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollector.java
index 2f6c538ea20..446517166ef 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollector.java
@@ -13,7 +13,7 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexNotFoundException;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -37,8 +37,8 @@ public class IndicesStatsCollector extends AbstractCollector {
 
     @Inject
     public IndicesStatsCollector(Settings settings, ClusterService clusterService,
-                                 MonitoringSettings monitoringSettings, MonitoringLicensee licensee, InternalClient client) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+                                 MonitoringSettings monitoringSettings, XPackLicenseState licenseState, InternalClient client) {
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
         this.client = client;
     }
 
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollector.java
index cbbeefb0836..5b855284c41 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollector.java
@@ -17,7 +17,7 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.env.NodeEnvironment;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -43,9 +43,9 @@ public class NodeStatsCollector extends AbstractCollector {
 
     @Inject
     public NodeStatsCollector(Settings settings, ClusterService clusterService, MonitoringSettings monitoringSettings,
-                              MonitoringLicensee licensee, InternalClient client,
+                              XPackLicenseState licenseState, InternalClient client,
                               NodeEnvironment nodeEnvironment, DiskThresholdDecider diskThresholdDecider) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
         this.client = client;
         this.nodeEnvironment = nodeEnvironment;
         this.diskThresholdDecider = diskThresholdDecider;
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollector.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollector.java
index 58ffd7097ac..c712d029267 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollector.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollector.java
@@ -13,7 +13,7 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.regex.Regex;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -36,8 +36,8 @@ public class ShardsCollector extends AbstractCollector {
 
     @Inject
     public ShardsCollector(Settings settings, ClusterService clusterService,
-                           MonitoringSettings monitoringSettings, MonitoringLicensee licensee) {
-        super(settings, NAME, clusterService, monitoringSettings, licensee);
+                           MonitoringSettings monitoringSettings, XPackLicenseState licenseState) {
+        super(settings, NAME, clusterService, monitoringSettings, licenseState);
     }
 
     @Override
diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java
index 524f494900f..c951efbc474 100644
--- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java
+++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java
@@ -13,8 +13,8 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.util.concurrent.AbstractLifecycleRunnable;
 import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
 import org.elasticsearch.common.util.concurrent.FutureUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.joda.time.DateTime;
 import org.joda.time.chrono.ISOChronology;
@@ -28,7 +28,7 @@ import java.util.concurrent.ScheduledFuture;
  */
 public class CleanerService extends AbstractLifecycleComponent {
 
-    private final MonitoringLicensee licensee;
+    private final XPackLicenseState licenseState;
     private final ThreadPool threadPool;
     private final ExecutionScheduler executionScheduler;
     private final List<Listener> listeners = new CopyOnWriteArrayList<>();
@@ -36,10 +36,10 @@ public class CleanerService extends AbstractLifecycleComponent {
 
     private volatile TimeValue globalRetention;
 
-    CleanerService(Settings settings, ClusterSettings clusterSettings, MonitoringLicensee licensee, ThreadPool threadPool,
+    CleanerService(Settings settings, ClusterSettings clusterSettings, XPackLicenseState licenseState, ThreadPool threadPool,
                    ExecutionScheduler executionScheduler) {
         super(settings);
-        this.licensee = licensee;
+        this.licenseState = licenseState;
         this.threadPool = threadPool;
         this.executionScheduler = executionScheduler;
         this.globalRetention = MonitoringSettings.HISTORY_DURATION.get(settings);
@@ -50,8 +50,8 @@ public class CleanerService extends AbstractLifecycleComponent {
     }
 
     @Inject
-    public CleanerService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool, MonitoringLicensee licensee) {
-        this(settings, clusterSettings, licensee,threadPool, new DefaultExecutionScheduler());
+    public CleanerService(Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool, XPackLicenseState licenseState) {
+        this(settings, clusterSettings, licenseState, threadPool, new DefaultExecutionScheduler());
     }
 
     @Override
@@ -85,11 +85,11 @@ public class CleanerService extends AbstractLifecycleComponent {
      * This will ignore the global retention if the license does not allow retention updates.
      *
      * @return Never {@code null}
-     * @see MonitoringLicensee#allowUpdateRetention()
+     * @see XPackLicenseState#isUpdateRetentionAllowed()
      */
     public TimeValue getRetention() {
         // we only care about their value if they are allowed to set it
-        if (licensee.allowUpdateRetention() && globalRetention != null) {
+        if (licenseState.isUpdateRetentionAllowed() && globalRetention != null) {
             return globalRetention;
         }
         else {
@@ -107,7 +107,7 @@ public class CleanerService extends AbstractLifecycleComponent {
      */
     public void setGlobalRetention(TimeValue globalRetention) {
         // notify the user that their setting will be ignored until they get the right license
-        if (licensee.allowUpdateRetention() == false) {
+        if (licenseState.isUpdateRetentionAllowed() == false) {
             logger.warn("[{}] setting will be ignored until an appropriate license is applied",
                     MonitoringSettings.HISTORY_DURATION.getKey());
         }
@@ -165,7 +165,7 @@ public class CleanerService extends AbstractLifecycleComponent {
 
         @Override
         protected void doRunInLifecycle() throws Exception {
-            if (licensee.cleaningEnabled() == false) {
+            if (licenseState.isMonitoringAllowed() == false) {
                 logger.debug("cleaning service is disabled due to invalid license");
                 return;
             }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java
index 457ddd951a5..fa3a417a852 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringFeatureSetTests.java
@@ -7,6 +7,7 @@ package org.elasticsearch.xpack.monitoring;
 
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.XPackFeatureSet;
 import org.elasticsearch.xpack.monitoring.agent.exporter.Exporter;
@@ -33,26 +34,26 @@ import static org.mockito.Mockito.when;
  */
 public class MonitoringFeatureSetTests extends ESTestCase {
 
-    private MonitoringLicensee licensee;
+    private XPackLicenseState licenseState;
     private NamedWriteableRegistry namedWriteableRegistry;
     private Exporters exporters;
 
     @Before
     public void init() throws Exception {
-        licensee = mock(MonitoringLicensee.class);
+        licenseState = mock(XPackLicenseState.class);
         exporters = mock(Exporters.class);
         namedWriteableRegistry = mock(NamedWriteableRegistry.class);
     }
 
     public void testWritableRegistration() throws Exception {
-        new MonitoringFeatureSet(Settings.EMPTY, licensee, exporters, namedWriteableRegistry);
+        new MonitoringFeatureSet(Settings.EMPTY, licenseState, exporters, namedWriteableRegistry);
         verify(namedWriteableRegistry).register(eq(MonitoringFeatureSet.Usage.class), eq("xpack.usage.monitoring"), anyObject());
     }
 
     public void testAvailable() throws Exception {
-        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licensee, exporters, namedWriteableRegistry);
+        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licenseState, exporters, namedWriteableRegistry);
         boolean available = randomBoolean();
-        when(licensee.isAvailable()).thenReturn(available);
+        when(licenseState.isMonitoringAllowed()).thenReturn(available);
         assertThat(featureSet.available(), is(available));
     }
 
@@ -60,12 +61,12 @@ public class MonitoringFeatureSetTests extends ESTestCase {
         boolean enabled = randomBoolean();
         Settings.Builder settings = Settings.builder();
         settings.put("xpack.monitoring.enabled", enabled);
-        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(settings.build(), licensee, exporters, namedWriteableRegistry);
+        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(settings.build(), licenseState, exporters, namedWriteableRegistry);
         assertThat(featureSet.enabled(), is(enabled));
     }
 
     public void testEnabledDefault() throws Exception {
-        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licensee, exporters, namedWriteableRegistry);
+        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licenseState, exporters, namedWriteableRegistry);
         assertThat(featureSet.enabled(), is(true));
     }
 
@@ -102,7 +103,7 @@ public class MonitoringFeatureSetTests extends ESTestCase {
         }
         when(exporters.iterator()).thenReturn(exporterList.iterator());
 
-        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licensee, exporters, namedWriteableRegistry);
+        MonitoringFeatureSet featureSet = new MonitoringFeatureSet(Settings.EMPTY, licenseState, exporters, namedWriteableRegistry);
         XPackFeatureSet.Usage usage = featureSet.usage();
         assertThat(usage.name(), is(featureSet.name()));
         assertThat(usage.enabled(), is(featureSet.enabled()));
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java
index b5a72d9cf03..aa4cdddba20 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java
@@ -5,55 +5,20 @@
  */
 package org.elasticsearch.xpack.monitoring.agent.collector;
 
-import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import com.carrotsearch.randomizedtesting.RandomizedTest;
-import com.carrotsearch.randomizedtesting.SysGlobals;
-import org.elasticsearch.action.ActionRequest;
-import org.elasticsearch.action.ActionResponse;
 import org.elasticsearch.cluster.block.ClusterBlocks;
-import org.elasticsearch.cluster.service.ClusterService;
-import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.common.inject.Module;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.env.Environment;
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.Licensing;
-import org.elasticsearch.license.plugin.core.LicenseService;
-import org.elasticsearch.license.plugin.core.Licensee;
-import org.elasticsearch.plugins.Plugin;
-import org.elasticsearch.rest.RestHandler;
 import org.elasticsearch.test.ESIntegTestCase;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
-import org.elasticsearch.watcher.ResourceWatcherService;
-import org.elasticsearch.xpack.XPackPlugin;
-import org.elasticsearch.xpack.graph.GraphLicensee;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase;
 import org.elasticsearch.xpack.security.InternalClient;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
-import org.elasticsearch.xpack.support.clock.Clock;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
-import org.junit.Before;
-
-import static java.util.Collections.emptyList;
-import static org.elasticsearch.common.unit.TimeValue.timeValueMinutes;
 
 @ClusterScope(scope = ESIntegTestCase.Scope.SUITE, randomDynamicTemplates = false, transportClientRatio = 0.0)
 public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase {
 
-    @Override
-    protected Collection<Class<? extends Plugin>> nodePlugins() {
-        return Arrays.asList(InternalXPackPlugin.class);
-    }
-
     @Override
     protected Settings nodeSettings(int nodeOrdinal) {
         return Settings.builder()
@@ -62,11 +27,6 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
                 .build();
     }
 
-    @Before
-    public void ensureLicenseIsEnabled() {
-        enableLicense();
-    }
-
     public InternalClient securedClient() {
         return internalCluster().getInstance(InternalClient.class);
     }
@@ -82,83 +42,6 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
         assertNotNull(results);
     }
 
-    protected void assertCannotCollect(AbstractCollector collector) {
-        assertNotNull(collector);
-        assertFalse("collector [" + collector.name() + "] should not be able to collect data", collector.shouldCollect());
-        Collection results = collector.collect();
-        assertTrue(results == null || results.isEmpty());
-    }
-
-    private static License createTestingLicense(long issueDate, long expiryDate) {
-        return License.builder()
-                .expiryDate(expiryDate)
-                .issueDate(issueDate)
-                .issuedTo("AbstractCollectorTestCase")
-                .issuer("test")
-                .maxNodes(Integer.MAX_VALUE)
-                .signature("_signature")
-                .type("trial")
-                .uid(String.valueOf(RandomizedTest.systemPropertyAsInt(SysGlobals.CHILDVM_SYSPROP_JVM_ID, 0)) +
-                        System.identityHashCode(AbstractCollectorTestCase.class))
-                .build();
-    }
-
-    protected static void enableLicense() {
-        long issueDate = System.currentTimeMillis();
-        long expiryDate = issueDate + randomDaysInMillis();
-
-        final License license = createTestingLicense(issueDate, expiryDate);
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.onChange(license.operationMode(), true);
-        }
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.update(license);
-        }
-    }
-
-    protected static void beginGracefulPeriod() {
-        long expiryDate = System.currentTimeMillis() + timeValueMinutes(10).millis();
-        long issueDate = expiryDate - randomDaysInMillis();
-
-        final License license = createTestingLicense(issueDate, expiryDate);
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.onChange(license.operationMode(), true);
-        }
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.update(license);
-        }
-    }
-
-    protected static void endGracefulPeriod() {
-        long expiryDate = System.currentTimeMillis() - MonitoringSettings.MAX_LICENSE_GRACE_PERIOD.millis() - timeValueMinutes(10).millis();
-        long issueDate = expiryDate - randomDaysInMillis();
-
-        final License license = createTestingLicense(issueDate, expiryDate);
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.onChange(license.operationMode(), false);
-        }
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.update(license);
-        }
-    }
-
-    protected static void disableLicense() {
-        long expiryDate = System.currentTimeMillis() - MonitoringSettings.MAX_LICENSE_GRACE_PERIOD.millis() - randomDaysInMillis();
-        long issueDate = expiryDate - randomDaysInMillis();
-
-        final License license = createTestingLicense(issueDate, expiryDate);
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.onChange(license.operationMode(), false);
-        }
-        for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
-            service.update(license);
-        }
-    }
-
-    private static long randomDaysInMillis() {
-        return TimeValue.timeValueHours(randomIntBetween(1, 30) * 24).millis();
-    }
-
     public void waitForNoBlocksOnNodes() throws Exception {
         assertBusy(new Runnable() {
             @Override
@@ -182,85 +65,4 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
             assertTrue(clusterBlocks.indices().values().isEmpty());
         }, 30L, TimeUnit.SECONDS);
     }
-
-    public static class InternalLicensing extends Licensing {
-
-        public InternalLicensing() {
-            super(Settings.EMPTY);
-        }
-
-        @Override
-        public Collection<Module> nodeModules() {
-            return Collections.singletonList(b -> b.bind(LicenseService.class).to(LicenseServiceForCollectors.class));
-        }
-
-        @Override
-        public Collection<Object> createComponents(ClusterService clusterService, Clock clock, Environment environment,
-                                                   ResourceWatcherService resourceWatcherService,
-                                                   SecurityLicenseState securityLicenseState) {
-            WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
-            MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
-            GraphLicensee graphLicensee = new GraphLicensee(settings);
-            LicenseService licenseService = new LicenseServiceForCollectors(settings, environment,
-                    resourceWatcherService, Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee));
-            return Arrays.asList(licenseService, watcherLicensee, monitoringLicensee, graphLicensee);
-        }
-
-        @Override
-        public List<ActionHandler<? extends ActionRequest<?>, ? extends ActionResponse>> getActions() {
-            return emptyList();
-        }
-
-        @Override
-        public List<Class<? extends RestHandler>> getRestHandlers() {
-            return emptyList();
-        }
-    }
-
-    public static class InternalXPackPlugin extends XPackPlugin {
-
-        public InternalXPackPlugin(Settings settings) throws IOException {
-            super(settings);
-            licensing = new InternalLicensing();
-        }
-    }
-
-    public static class LicenseServiceForCollectors extends LicenseService {
-
-        private final List<Licensee> licensees;
-        private volatile License license;
-
-        @Inject
-        public LicenseServiceForCollectors(Settings settings, Environment env,
-                                           ResourceWatcherService resourceWatcherService, List<Licensee> licensees) {
-            super(settings, null, null, env, resourceWatcherService, licensees);
-            this.licensees = licensees;
-        }
-
-        public void onChange(License.OperationMode operationMode, boolean active) {
-            for (Licensee licensee : licensees) {
-                licensee.onChange(new Licensee.Status(operationMode, active));
-            }
-        }
-
-        @Override
-        public Licensee.Status licenseeStatus(License license) {
-            return null;
-        }
-
-        @Override
-        public License getLicense() {
-            return license;
-        }
-
-        public synchronized void update(License license) {
-            this.license = license;
-        }
-
-        @Override
-        protected void doStart() {}
-
-        @Override
-        protected void doStop() {}
-    }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollectorTests.java
index f7e92724ee5..0823b3ba3c3 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStateCollectorTests.java
@@ -10,8 +10,8 @@ import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -106,44 +106,6 @@ public class ClusterStateCollectorTests extends AbstractCollectorTestCase {
         }
     }
 
-    public void testClusterStateCollectorWithLicensing() {
-        try {
-            String[] nodes = internalCluster().getNodeNames();
-            for (String node : nodes) {
-                logger.debug("--> creating a new instance of the collector");
-                ClusterStateCollector collector = newClusterStateCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data if node is master");
-                enableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data if node is master");
-                beginGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> ending graceful period and checks that the collector cannot collect data");
-                endGracefulPeriod();
-                assertCannotCollect(collector);
-
-                logger.debug("--> disabling license and checks that the collector cannot collect data");
-                disableLicense();
-                assertCannotCollect(collector);
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     private ClusterStateCollector newClusterStateCollector() {
         // This collector runs on master node only
         return newClusterStateCollector(internalCluster().getMasterName());
@@ -154,7 +116,7 @@ public class ClusterStateCollectorTests extends AbstractCollectorTestCase {
         return new ClusterStateCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId),
+                internalCluster().getInstance(XPackLicenseState.class, nodeId),
                 securedClient(nodeId));
     }
 
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollectorTests.java
index 52dacb48da7..370fa9f642a 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/cluster/ClusterStatsCollectorTests.java
@@ -12,8 +12,8 @@ import org.elasticsearch.Version;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.license.plugin.core.LicenseService;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
@@ -75,52 +75,6 @@ public class ClusterStatsCollectorTests extends AbstractCollectorTestCase {
                 equalTo(internalCluster().getNodeNames().length));
     }
 
-    public void testClusterStatsCollectorWithLicensing() {
-        try {
-            String[] nodes = internalCluster().getNodeNames();
-            for (String node : nodes) {
-                logger.debug("--> creating a new instance of the collector");
-                ClusterStatsCollector collector = newClusterStatsCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data if node is master");
-                enableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector, ClusterInfoMonitoringDoc.class, ClusterStatsMonitoringDoc.class);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data if node is master");
-                beginGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector, ClusterInfoMonitoringDoc.class, ClusterStatsMonitoringDoc.class);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> ending graceful period and checks that the collector can still collect data (if node is master)");
-                endGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector, ClusterInfoMonitoringDoc.class);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> disabling license and checks that the collector can still collect data (if node is master)");
-                disableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector, ClusterInfoMonitoringDoc.class);
-                } else {
-                    assertCannotCollect(collector);
-                }
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     private ClusterStatsCollector newClusterStatsCollector() {
         // This collector runs on master node only
         return newClusterStatsCollector(internalCluster().getMasterName());
@@ -131,7 +85,7 @@ public class ClusterStatsCollectorTests extends AbstractCollectorTestCase {
         return new ClusterStatsCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId),
+                internalCluster().getInstance(XPackLicenseState.class, nodeId),
                 securedClient(nodeId),
                 internalCluster().getInstance(LicenseService.class, nodeId));
     }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollectorTests.java
index 86c24fa2b25..fdd37852e3f 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexRecoveryCollectorTests.java
@@ -13,10 +13,10 @@ import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexNotFoundException;
 import org.elasticsearch.indices.recovery.RecoveryState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
 import org.elasticsearch.test.hamcrest.ElasticsearchAssertions;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -130,46 +130,6 @@ public class IndexRecoveryCollectorTests extends AbstractCollectorTestCase {
         }
     }
 
-    public void testIndexRecoveryCollectorWithLicensing() throws Exception {
-        List<String> nodesIds = internalCluster().startNodesAsync(randomIntBetween(2, 5)).get();
-        waitForNoBlocksOnNodes();
-
-        try {
-            for (String node : nodesIds) {
-                logger.debug("--> creating a new instance of the collector");
-                IndexRecoveryCollector collector = newIndexRecoveryCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data if node is master");
-                enableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data if node is master");
-                beginGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> ending graceful period and checks that the collector cannot collect data");
-                endGracefulPeriod();
-                assertCannotCollect(collector);
-
-                logger.debug("--> disabling license and checks that the collector cannot collect data");
-                disableLicense();
-                assertCannotCollect(collector);
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     public void testEmptyCluster() throws Exception {
         final String node = internalCluster().startNode(Settings.builder().put(MonitoringSettings.INDICES.getKey(),
                 Strings.EMPTY_ARRAY));
@@ -211,7 +171,7 @@ public class IndexRecoveryCollectorTests extends AbstractCollectorTestCase {
         return new IndexRecoveryCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId),
+                internalCluster().getInstance(XPackLicenseState.class, nodeId),
                 securedClient(nodeId));
     }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollectorTests.java
index bbfa57e01d4..3d7cd3bda92 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndexStatsCollectorTests.java
@@ -11,9 +11,9 @@ import org.elasticsearch.cluster.metadata.MetaData;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexNotFoundException;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -181,55 +181,6 @@ public class IndexStatsCollectorTests extends AbstractCollectorTestCase {
         }
     }
 
-    public void testIndexStatsCollectorWithLicensing() throws Exception {
-        List<String> nodesIds = internalCluster().startNodesAsync(randomIntBetween(2, 5)).get();
-        waitForNoBlocksOnNodes();
-
-        try {
-            final int nbDocs = randomIntBetween(1, 20);
-            for (int i = 0; i < nbDocs; i++) {
-                client().prepareIndex("test", "test").setSource("num", i).get();
-            }
-
-            securedFlush();
-            securedRefresh();
-            securedEnsureGreen("test");
-
-            for (String node : nodesIds) {
-                logger.debug("--> creating a new instance of the collector");
-                IndexStatsCollector collector = newIndexStatsCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data if node is master");
-                enableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data if node is master");
-                beginGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> ending graceful period and checks that the collector cannot collect data");
-                endGracefulPeriod();
-                assertCannotCollect(collector);
-
-                logger.debug("--> disabling license and checks that the collector cannot collect data");
-                disableLicense();
-                assertCannotCollect(collector);
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     private IndexStatsCollector newIndexStatsCollector() {
         // This collector runs on master node only
         return newIndexStatsCollector(internalCluster().getMasterName());
@@ -240,7 +191,7 @@ public class IndexStatsCollectorTests extends AbstractCollectorTestCase {
         return new IndexStatsCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId),
+                internalCluster().getInstance(XPackLicenseState.class, nodeId),
                 securedClient(nodeId));
     }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollectorTests.java
index bd4706ec065..8efc0ff1b11 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/indices/IndicesStatsCollectorTests.java
@@ -13,9 +13,9 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexNotFoundException;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -155,55 +155,6 @@ public class IndicesStatsCollectorTests extends AbstractCollectorTestCase {
         assertThat(indicesStats.getIndices().keySet(), hasSize(nbIndices));
     }
 
-    public void testIndicesStatsCollectorWithLicensing() throws Exception {
-        List<String> nodesIds = internalCluster().startNodesAsync(randomIntBetween(2, 5)).get();
-        waitForNoBlocksOnNodes();
-
-        try {
-            final int nbDocs = randomIntBetween(1, 20);
-            for (int i = 0; i < nbDocs; i++) {
-                client().prepareIndex("test", "test").setSource("num", i).get();
-            }
-
-            securedFlush();
-            securedRefresh();
-            securedEnsureGreen("test");
-
-            for (String node : nodesIds) {
-                logger.debug("--> creating a new instance of the collector");
-                IndicesStatsCollector collector = newIndicesStatsCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data if node is master");
-                enableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data if node is master");
-                beginGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> ending graceful period and checks that the collector cannot collect data");
-                endGracefulPeriod();
-                assertCannotCollect(collector);
-
-                logger.debug("--> disabling license and checks that the collector cannot collect data");
-                disableLicense();
-                assertCannotCollect(collector);
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     private IndicesStatsCollector newIndicesStatsCollector() {
         // This collector runs on master node only
         return newIndicesStatsCollector(internalCluster().getMasterName());
@@ -216,7 +167,7 @@ public class IndicesStatsCollectorTests extends AbstractCollectorTestCase {
         return new IndicesStatsCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId),
+                internalCluster().getInstance(XPackLicenseState.class, nodeId),
                 securedClient(nodeId));
     }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollectorTests.java
index c2a9067f579..8d503b2b7be 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/node/NodeStatsCollectorTests.java
@@ -11,9 +11,9 @@ import org.elasticsearch.cluster.routing.allocation.decider.DiskThresholdDecider
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.env.NodeEnvironment;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -63,41 +63,11 @@ public class NodeStatsCollectorTests extends AbstractCollectorTestCase {
         }
     }
 
-    public void testNodeStatsCollectorWithLicensing() {
-        try {
-            String[] nodes = internalCluster().getNodeNames();
-            for (String node : nodes) {
-                logger.debug("--> creating a new instance of the collector");
-                NodeStatsCollector collector = newNodeStatsCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data");
-                enableLicense();
-                assertCanCollect(collector);
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data");
-                beginGracefulPeriod();
-                assertCanCollect(collector);
-
-                logger.debug("--> ending graceful period and checks that the collector cannot collect data");
-                endGracefulPeriod();
-                assertCannotCollect(collector);
-
-                logger.debug("--> disabling license and checks that the collector cannot collect data");
-                disableLicense();
-                assertCannotCollect(collector);
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     private NodeStatsCollector newNodeStatsCollector(final String nodeId) {
         return new NodeStatsCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId),
+                internalCluster().getInstance(XPackLicenseState.class, nodeId),
                 internalCluster().getInstance(InternalClient.class, nodeId),
                 internalCluster().getInstance(NodeEnvironment.class, nodeId),
                 internalCluster().getInstance(DiskThresholdDecider.class, nodeId));
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollectorTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollectorTests.java
index 6ea921ed419..17aa341cffc 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollectorTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/shards/ShardsCollectorTests.java
@@ -10,8 +10,8 @@ import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.routing.ShardRouting;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.MonitoredSystem;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollectorTestCase;
 import org.elasticsearch.xpack.monitoring.agent.exporter.MonitoringDoc;
@@ -160,44 +160,6 @@ public class ShardsCollectorTests extends AbstractCollectorTestCase {
         }
     }
 
-    public void testShardsCollectorWithLicensing() {
-        try {
-            String[] nodes = internalCluster().getNodeNames();
-            for (String node : nodes) {
-                logger.debug("--> creating a new instance of the collector");
-                ShardsCollector collector = newShardsCollector(node);
-                assertNotNull(collector);
-
-                logger.debug("--> enabling license and checks that the collector can collect data if node is master");
-                enableLicense();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> starting graceful period and checks that the collector can still collect data if node is master");
-                beginGracefulPeriod();
-                if (node.equals(internalCluster().getMasterName())) {
-                    assertCanCollect(collector);
-                } else {
-                    assertCannotCollect(collector);
-                }
-
-                logger.debug("--> ending graceful period and checks that the collector cannot collect data");
-                endGracefulPeriod();
-                assertCannotCollect(collector);
-
-                logger.debug("--> disabling license and checks that the collector cannot collect data");
-                disableLicense();
-                assertCannotCollect(collector);
-            }
-        } finally {
-            // Ensure license is enabled before finishing the test
-            enableLicense();
-        }
-    }
-
     private ShardsCollector newShardsCollector() {
         // This collector runs on master node only
         return newShardsCollector(internalCluster().getMasterName());
@@ -208,6 +170,6 @@ public class ShardsCollectorTests extends AbstractCollectorTestCase {
         return new ShardsCollector(internalCluster().getInstance(Settings.class, nodeId),
                 internalCluster().getInstance(ClusterService.class, nodeId),
                 internalCluster().getInstance(MonitoringSettings.class, nodeId),
-                internalCluster().getInstance(MonitoringLicensee.class, nodeId));
+                internalCluster().getInstance(XPackLicenseState.class, nodeId));
     }
 }
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerServiceTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerServiceTests.java
index 89f2b72af5e..e9b0f4f62c8 100644
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerServiceTests.java
+++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerServiceTests.java
@@ -8,10 +8,10 @@ package org.elasticsearch.xpack.monitoring.cleaner;
 import org.elasticsearch.common.settings.ClusterSettings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.threadpool.TestThreadPool;
 import org.elasticsearch.threadpool.ThreadPool;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
 import org.elasticsearch.xpack.monitoring.MonitoringSettings;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -34,7 +34,7 @@ public class CleanerServiceTests extends ESTestCase {
     @Rule
     public ExpectedException expectedException = ExpectedException.none();
 
-    private final MonitoringLicensee licensee = mock(MonitoringLicensee.class);
+    private final XPackLicenseState licenseState = mock(XPackLicenseState.class);
     private ClusterSettings clusterSettings;
     private ThreadPool threadPool;
 
@@ -56,39 +56,39 @@ public class CleanerServiceTests extends ESTestCase {
         TimeValue expected = TimeValue.timeValueHours(1);
         Settings settings = Settings.builder().put(MonitoringSettings.HISTORY_DURATION.getKey(), expected.getStringRep()).build();
 
-        new CleanerService(settings, clusterSettings, threadPool, licensee);
+        new CleanerService(settings, clusterSettings, threadPool, licenseState);
     }
 
     public void testGetRetentionWithSettingWithUpdatesAllowed() {
         TimeValue expected = TimeValue.timeValueHours(25);
         Settings settings = Settings.builder().put(MonitoringSettings.HISTORY_DURATION.getKey(), expected.getStringRep()).build();
 
-        when(licensee.allowUpdateRetention()).thenReturn(true);
+        when(licenseState.isUpdateRetentionAllowed()).thenReturn(true);
 
-        assertEquals(expected, new CleanerService(settings, clusterSettings, threadPool, licensee).getRetention());
+        assertEquals(expected, new CleanerService(settings, clusterSettings, threadPool, licenseState).getRetention());
 
-        verify(licensee).allowUpdateRetention();
+        verify(licenseState).isUpdateRetentionAllowed();
     }
 
     public void testGetRetentionDefaultValueWithNoSettings() {
-        when(licensee.allowUpdateRetention()).thenReturn(true);
+        when(licenseState.isUpdateRetentionAllowed()).thenReturn(true);
 
         assertEquals(MonitoringSettings.HISTORY_DURATION.get(Settings.EMPTY),
-                     new CleanerService(Settings.EMPTY, clusterSettings, threadPool, licensee).getRetention());
+                     new CleanerService(Settings.EMPTY, clusterSettings, threadPool, licenseState).getRetention());
 
-        verify(licensee).allowUpdateRetention();
+        verify(licenseState).isUpdateRetentionAllowed();
     }
 
     public void testGetRetentionDefaultValueWithSettingsButUpdatesNotAllowed() {
         TimeValue notExpected = TimeValue.timeValueHours(25);
         Settings settings = Settings.builder().put(MonitoringSettings.HISTORY_DURATION.getKey(), notExpected.getStringRep()).build();
 
-        when(licensee.allowUpdateRetention()).thenReturn(false);
+        when(licenseState.isUpdateRetentionAllowed()).thenReturn(false);
 
         assertEquals(MonitoringSettings.HISTORY_DURATION.get(Settings.EMPTY),
-                     new CleanerService(settings, clusterSettings, threadPool, licensee).getRetention());
+                     new CleanerService(settings, clusterSettings, threadPool, licenseState).getRetention());
 
-        verify(licensee).allowUpdateRetention();
+        verify(licenseState).isUpdateRetentionAllowed();
     }
 
     public void testSetGlobalRetention() {
@@ -96,15 +96,15 @@ public class CleanerServiceTests extends ESTestCase {
         // only thing calling this method and it will use the settings object to validate the time value
         TimeValue expected = TimeValue.timeValueHours(2);
 
-        when(licensee.allowUpdateRetention()).thenReturn(true);
+        when(licenseState.isUpdateRetentionAllowed()).thenReturn(true);
 
-        CleanerService service = new CleanerService(Settings.EMPTY, clusterSettings, threadPool, licensee);
+        CleanerService service = new CleanerService(Settings.EMPTY, clusterSettings, threadPool, licenseState);
 
         service.setGlobalRetention(expected);
 
         assertEquals(expected, service.getRetention());
 
-        verify(licensee, times(2)).allowUpdateRetention(); // once by set, once by get
+        verify(licenseState, times(2)).isUpdateRetentionAllowed(); // once by set, once by get
     }
 
     public void testSetGlobalRetentionAppliesEvenIfLicenseDisallows() {
@@ -113,9 +113,9 @@ public class CleanerServiceTests extends ESTestCase {
         TimeValue expected = TimeValue.timeValueHours(2);
 
         // required to be true on the second call for it to see it take effect
-        when(licensee.allowUpdateRetention()).thenReturn(false).thenReturn(true);
+        when(licenseState.isUpdateRetentionAllowed()).thenReturn(false).thenReturn(true);
 
-        CleanerService service = new CleanerService(Settings.EMPTY, clusterSettings, threadPool, licensee);
+        CleanerService service = new CleanerService(Settings.EMPTY, clusterSettings, threadPool, licenseState);
 
         // uses allow=false
         service.setGlobalRetention(expected);
@@ -123,7 +123,7 @@ public class CleanerServiceTests extends ESTestCase {
         // uses allow=true
         assertEquals(expected, service.getRetention());
 
-        verify(licensee, times(2)).allowUpdateRetention();
+        verify(licenseState, times(2)).isUpdateRetentionAllowed();
     }
 
     public void testNextExecutionDelay() {
@@ -151,9 +151,9 @@ public class CleanerServiceTests extends ESTestCase {
         CountDownLatch latch = new CountDownLatch(nbExecutions);
 
         logger.debug("--> creates a cleaner service that cleans every second");
-        MonitoringLicensee licensee = mock(MonitoringLicensee.class);
-        when(licensee.cleaningEnabled()).thenReturn(true);
-        CleanerService service = new CleanerService(Settings.EMPTY, clusterSettings, licensee, threadPool,
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isMonitoringAllowed()).thenReturn(true);
+        CleanerService service = new CleanerService(Settings.EMPTY, clusterSettings, licenseState, threadPool,
                 new TestExecutionScheduler(1_000));
 
         logger.debug("--> registers cleaning listener");
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java
deleted file mode 100644
index 422af9070af..00000000000
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java
+++ /dev/null
@@ -1,168 +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.xpack.monitoring.license;
-
-import org.elasticsearch.action.ActionRequest;
-import org.elasticsearch.action.ActionResponse;
-import org.elasticsearch.cluster.service.ClusterService;
-import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.common.inject.Module;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.env.Environment;
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.Licensing;
-import org.elasticsearch.license.plugin.core.LicenseService;
-import org.elasticsearch.license.plugin.core.Licensee;
-import org.elasticsearch.plugins.Plugin;
-import org.elasticsearch.rest.RestHandler;
-import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
-import org.elasticsearch.watcher.ResourceWatcherService;
-import org.elasticsearch.xpack.XPackPlugin;
-import org.elasticsearch.xpack.graph.GraphLicensee;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
-import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
-import org.elasticsearch.xpack.support.clock.Clock;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import static java.util.Collections.emptyList;
-import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-
-@ClusterScope(scope = SUITE, transportClientRatio = 0, numClientNodes = 0)
-public class LicenseIntegrationTests extends MonitoringIntegTestCase {
-    @Override
-    protected Collection<Class<? extends Plugin>> nodePlugins() {
-        return Arrays.asList(InternalXPackPlugin.class);
-    }
-
-    @Override
-    protected Settings nodeSettings(int nodeOrdinal) {
-        return Settings.builder()
-                .put(super.nodeSettings(nodeOrdinal))
-                .build();
-    }
-
-    public void testEnableDisableLicense() {
-        assertTrue(getLicensee().getStatus().isActive());
-        assertThat(getLicensee().collectionEnabled(), is(true));
-        disableLicensing();
-
-        assertThat(getLicensee().getStatus().isActive(), equalTo(false));
-        assertThat(getLicensee().collectionEnabled(), is(false));
-        enableLicensing();
-
-        assertTrue(getLicensee().getStatus().isActive());
-        assertThat(getLicensee().collectionEnabled(), is(true));
-    }
-
-    private MonitoringLicensee getLicensee() {
-        MonitoringLicensee licensee = internalCluster().getInstance(MonitoringLicensee.class);
-        assertNotNull(licensee);
-        return licensee;
-    }
-
-    public static void disableLicensing() {
-        for (MockLicenseService service : internalCluster().getInstances(MockLicenseService.class)) {
-            service.disable();
-        }
-    }
-
-    public static void enableLicensing() {
-        for (MockLicenseService service : internalCluster().getInstances(MockLicenseService.class)) {
-            service.enable();
-        }
-    }
-
-    public static class MockLicensing extends Licensing {
-
-        public MockLicensing() {
-            super(Settings.EMPTY);
-        }
-
-        @Override
-        public Collection<Module> nodeModules() {
-            return Collections.singletonList(b -> b.bind(LicenseService.class).to(MockLicenseService.class));
-        }
-
-        @Override
-        public Collection<Object> createComponents(ClusterService clusterService, Clock clock, Environment environment,
-                                                   ResourceWatcherService resourceWatcherService,
-                                                   SecurityLicenseState securityLicenseState) {
-            WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
-            MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
-            GraphLicensee graphLicensee = new GraphLicensee(settings);
-            LicenseService licenseService = new MockLicenseService(settings, environment, resourceWatcherService,
-                    Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee));
-            return Arrays.asList(licenseService, watcherLicensee, monitoringLicensee, graphLicensee);
-        }
-
-        @Override
-        public List<ActionHandler<? extends ActionRequest<?>, ? extends ActionResponse>> getActions() {
-            return emptyList();
-        }
-
-        @Override
-        public List<Class<? extends RestHandler>> getRestHandlers() {
-            return emptyList();
-        }
-    }
-
-    public static class MockLicenseService extends LicenseService {
-
-        private final List<Licensee> licensees;
-
-        @Inject
-        public MockLicenseService(Settings settings, Environment environment,
-                                  ResourceWatcherService resourceWatcherService, List<Licensee> licensees) {
-            super(settings, null, null, environment, resourceWatcherService, licensees);
-            this.licensees = licensees;
-            enable();
-        }
-
-        public void enable() {
-            for (Licensee licensee : licensees) {
-                licensee.onChange(new Licensee.Status(License.OperationMode.BASIC, true));
-            }
-        }
-
-        public void disable() {
-            for (Licensee licensee : licensees) {
-                licensee.onChange(new Licensee.Status(License.OperationMode.BASIC, false));
-            }
-        }
-
-        @Override
-        public Licensee.Status licenseeStatus(License license) {
-            return null;
-        }
-
-        @Override
-        public License getLicense() {
-            return null;
-        }
-
-        @Override
-        protected void doStart() {}
-
-        @Override
-        protected void doStop() {}
-    }
-
-    public static class InternalXPackPlugin extends XPackPlugin {
-        public InternalXPackPlugin(Settings settings) throws IOException {
-            super(settings);
-            licensing = new MockLicensing();
-        }
-    }
-}
diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/MonitoringLicenseeTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/MonitoringLicenseeTests.java
deleted file mode 100644
index b6844190c77..00000000000
--- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/MonitoringLicenseeTests.java
+++ /dev/null
@@ -1,111 +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.xpack.monitoring.license;
-
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeTestCase;
-import org.elasticsearch.license.plugin.core.Licensee.Status;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
-
-import java.util.function.Predicate;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * Tests {@link MonitoringLicensee}.
- * <p>
- * If you change the behavior of these tests, then it means that licensing changes for Monitoring!
- */
-public class MonitoringLicenseeTests extends AbstractLicenseeTestCase {
-    private final MonitoringLicensee licensee = new MonitoringLicensee(Settings.EMPTY);
-
-    public void testAcknowledgementMessagesToAnyFromFreeIsNoOp() {
-        assertEmptyAck(OperationMode.BASIC, randomMode(), licensee);
-    }
-
-    public void testAcknowledgementMessagesToTrialGoldOrPlatinumFromAnyIsNoOp() {
-        assertEmptyAck(randomMode(), randomTrialStandardGoldOrPlatinumMode(), licensee);
-    }
-
-    public void testAcknowledgementMessagesToBasicFromNotBasicNotesLimits() {
-        OperationMode from = randomFrom(OperationMode.STANDARD, OperationMode.GOLD, OperationMode.PLATINUM, OperationMode.TRIAL);
-        OperationMode to = OperationMode.BASIC;
-
-        String[] messages = ackLicenseChange(from, to, licensee);
-
-        // leaving messages up to inspection
-        assertThat(fromToMessage(from, to), messages.length, equalTo(2));
-    }
-
-    public void testCollectionEnabledIsTrueForActiveState() {
-        assertEnabled(true, MonitoringLicensee::collectionEnabled, true);
-    }
-
-    public void testCollectionEnabledIsFalseForInactiveState() {
-        assertEnabled(false, MonitoringLicensee::collectionEnabled, false);
-    }
-
-    public void testCleaningEnabledIsTrueForActiveState() {
-        assertEnabled(true, MonitoringLicensee::cleaningEnabled, true);
-    }
-
-    public void testCleaningEnabledIsFalseForInactiveState() {
-        assertEnabled(false, MonitoringLicensee::cleaningEnabled, false);
-    }
-
-    public void testAllowUpdateRetentionIsTrueForNotBasic() {
-        OperationMode mode = randomFrom(OperationMode.STANDARD, OperationMode.GOLD, OperationMode.PLATINUM, OperationMode.TRIAL);
-        assertEnabled(mode, MonitoringLicensee::allowUpdateRetention, true);
-    }
-
-    public void testAllowUpdateRetentionIsFalseForBasic() {
-        assertEnabled(OperationMode.BASIC, MonitoringLicensee::allowUpdateRetention, false);
-    }
-
-    public void testAllowUpdateRetentionIsFalseForMissing() {
-        assertEnabled(OperationMode.MISSING, MonitoringLicensee::allowUpdateRetention, false);
-    }
-
-    /**
-     * Assert that the {@link #licensee} is {@code predicate}d as {@code expected} when setting the {@code state}.
-     *
-     * @param active The state that should cause the {@code expected} {@code predicate}.
-     * @param predicate The method to invoke (expected to be an instance method).
-     * @param expected The expected outcome given the {@code state} and {@code predicate}.
-     */
-    private void assertEnabled(boolean active, Predicate<MonitoringLicensee> predicate, boolean expected) {
-        Status status = mock(Status.class);
-        when(status.isActive()).thenReturn(active);
-
-        licensee.onChange(status);
-
-        assertThat(predicate.test(licensee), equalTo(expected));
-
-        verify(status).isActive();
-    }
-
-    /**
-     * Assert that the {@link #licensee} is {@code predicate}d as {@code expected} when setting the {@code mode}.
-     *
-     * @param mode The mode that should cause the {@code expected} {@code predicate}.
-     * @param predicate The method to invoke (expected to be an instance method).
-     * @param expected The expected outcome given the {@code mode} and {@code predicate}.
-     */
-    private void assertEnabled(OperationMode mode, Predicate<MonitoringLicensee> predicate, boolean expected) {
-        Status status = mock(Status.class);
-        when(status.getMode()).thenReturn(mode);
-
-        licensee.onChange(status);
-
-        assertThat(predicate.test(licensee), equalTo(expected));
-
-        verify(status).getMode();
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java
index 68c5dbfed0a..e9ef647a048 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/Security.java
@@ -40,6 +40,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.env.Environment;
 import org.elasticsearch.index.IndexModule;
 import org.elasticsearch.ingest.Processor;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.plugins.ActionPlugin;
 import org.elasticsearch.plugins.IngestPlugin;
 import org.elasticsearch.rest.RestHandler;
@@ -89,10 +90,10 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
 import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
 import org.elasticsearch.xpack.security.authc.support.SecuredString;
 import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
-import org.elasticsearch.xpack.security.authz.AuthorizationService;
-import org.elasticsearch.xpack.security.authz.accesscontrol.SetSecurityUserProcessor;
 import org.elasticsearch.xpack.security.authz.accesscontrol.OptOutQueryCache;
 import org.elasticsearch.xpack.security.authz.accesscontrol.SecurityIndexSearcherWrapper;
+import org.elasticsearch.xpack.security.authz.accesscontrol.SetSecurityUserProcessor;
+import org.elasticsearch.xpack.security.authz.AuthorizationService;
 import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
 import org.elasticsearch.xpack.security.authz.store.FileRolesStore;
 import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
@@ -149,10 +150,10 @@ public class Security implements ActionPlugin, IngestPlugin {
     private final Environment env;
     private final boolean enabled;
     private final boolean transportClientMode;
-    private final SecurityLicenseState securityLicenseState;
+    private final XPackLicenseState licenseState;
     private final CryptoService cryptoService;
 
-    public Security(Settings settings, Environment env) throws IOException {
+    public Security(Settings settings, Environment env, XPackLicenseState licenseState) throws IOException {
         this.settings = settings;
         this.env = env;
         this.transportClientMode = XPackPlugin.transportClientMode(settings);
@@ -163,17 +164,13 @@ public class Security implements ActionPlugin, IngestPlugin {
         } else {
             cryptoService = null;
         }
-        securityLicenseState = new SecurityLicenseState();
+        this.licenseState = licenseState;
     }
 
     public CryptoService getCryptoService() {
         return cryptoService;
     }
 
-    public SecurityLicenseState getSecurityLicenseState() {
-        return securityLicenseState;
-    }
-
     public boolean isEnabled() {
         return enabled;
     }
@@ -203,7 +200,7 @@ public class Security implements ActionPlugin, IngestPlugin {
                 b.bind(Realms.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
                 b.bind(CompositeRolesStore.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
                 b.bind(AuditTrailService.class)
-                    .toInstance(new AuditTrailService(settings, Collections.emptyList(), securityLicenseState));
+                    .toInstance(new AuditTrailService(settings, Collections.emptyList(), licenseState));
             });
             modules.add(new SecurityTransportModule(settings));
             return modules;
@@ -259,7 +256,7 @@ public class Security implements ActionPlugin, IngestPlugin {
                 }
             }
         }
-        final Realms realms = new Realms(settings, env, realmFactories, securityLicenseState, reservedRealm);
+        final Realms realms = new Realms(settings, env, realmFactories, licenseState, reservedRealm);
         components.add(nativeUsersStore);
         components.add(realms);
 
@@ -288,7 +285,7 @@ public class Security implements ActionPlugin, IngestPlugin {
             }
         }
         final AuditTrailService auditTrailService =
-            new AuditTrailService(settings, auditTrails.stream().collect(Collectors.toList()), securityLicenseState);
+            new AuditTrailService(settings, auditTrails.stream().collect(Collectors.toList()), licenseState);
         components.add(auditTrailService);
 
         AuthenticationFailureHandler failureHandler = null;
@@ -425,12 +422,13 @@ public class Security implements ActionPlugin, IngestPlugin {
             return;
         }
 
-        assert securityLicenseState != null;
+        assert licenseState != null;
         if (flsDlsEnabled(settings)) {
-            module.setSearcherWrapper((indexService) -> new SecurityIndexSearcherWrapper(indexService.getIndexSettings(),
-                    indexService.newQueryShardContext(), indexService.mapperService(),
-                    indexService.cache().bitsetFilterCache(), indexService.getIndexServices().getThreadPool().getThreadContext(),
-                    securityLicenseState, indexService.getIndexServices().getScriptService()));
+            module.setSearcherWrapper(indexService ->
+                new SecurityIndexSearcherWrapper(indexService.getIndexSettings(), indexService.newQueryShardContext(),
+                    indexService.mapperService(), indexService.cache().bitsetFilterCache(),
+                    indexService.getIndexServices().getThreadPool().getThreadContext(), licenseState,
+                    indexService.getIndexServices().getScriptService()));
         }
         if (transportClientMode == false) {
             /*  We need to forcefully overwrite the query cache implementation to use security's opt out query cache implementation.
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java
index 871615149fd..14fb4070264 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java
@@ -13,6 +13,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.Writeable;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.security.audit.AuditTrailService;
 import org.elasticsearch.xpack.security.authc.Realm;
 import org.elasticsearch.xpack.security.authc.Realms;
@@ -40,7 +41,7 @@ public class SecurityFeatureSet implements XPackFeatureSet {
 
     private final Settings settings;
     private final boolean enabled;
-    private final SecurityLicenseState licenseState;
+    private final XPackLicenseState licenseState;
     @Nullable
     private final Realms realms;
     @Nullable
@@ -53,7 +54,7 @@ public class SecurityFeatureSet implements XPackFeatureSet {
     private final CryptoService cryptoService;
 
     @Inject
-    public SecurityFeatureSet(Settings settings, @Nullable SecurityLicenseState licenseState, @Nullable Realms realms,
+    public SecurityFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState, @Nullable Realms realms,
                               NamedWriteableRegistry namedWriteableRegistry, @Nullable CompositeRolesStore rolesStore,
                               @Nullable IPFilter ipFilter, @Nullable AuditTrailService auditTrailService,
                               @Nullable CryptoService cryptoService) {
@@ -80,7 +81,7 @@ public class SecurityFeatureSet implements XPackFeatureSet {
 
     @Override
     public boolean available() {
-        return licenseState != null && licenseState.authenticationAndAuthorizationEnabled();
+        return licenseState != null && licenseState.isAuthAllowed();
     }
 
     @Override
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicenseState.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicenseState.java
deleted file mode 100644
index 19e76e230f5..00000000000
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicenseState.java
+++ /dev/null
@@ -1,103 +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.xpack.security;
-
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.Licensee.Status;
-
-
-/**
- * This class serves to decouple security code that needs to check the license state from the {@link SecurityLicensee} as the
- * tight coupling causes issues with guice injection and circular dependencies
- */
-public class SecurityLicenseState {
-
-    // we initialize the licensee status to enabled with trial operation mode to ensure no
-    // legitimate requests are blocked before initial license plugin notification
-    protected volatile Status status = Status.ENABLED;
-
-    /**
-     * @return true if authentication and authorization should be enabled. this does not indicate what realms are available
-     * @see SecurityLicenseState#enabledRealmType() for the enabled realms
-     */
-    public boolean authenticationAndAuthorizationEnabled() {
-        OperationMode mode = status.getMode();
-        return mode == OperationMode.STANDARD || mode == OperationMode.GOLD || mode == OperationMode.PLATINUM
-                || mode == OperationMode.TRIAL;
-    }
-
-    /**
-     * @return true if IP filtering should be enabled
-     */
-    public boolean ipFilteringEnabled() {
-        OperationMode mode = status.getMode();
-        return mode == OperationMode.GOLD || mode == OperationMode.PLATINUM || mode == OperationMode.TRIAL;
-    }
-
-    /**
-     * @return true if auditing should be enabled
-     */
-    public boolean auditingEnabled() {
-        OperationMode mode = status.getMode();
-        return mode == OperationMode.GOLD || mode == OperationMode.PLATINUM || mode == OperationMode.TRIAL;
-    }
-
-    /**
-     * Indicates whether the stats and health API calls should be allowed. If a license is expired and past the grace
-     * period then we deny these calls.
-     *
-     * @return true if the license allows for the stats and health APIs to be used.
-     */
-    public boolean statsAndHealthEnabled() {
-        return status.isActive();
-    }
-
-    /**
-     * Determine if Document Level Security (DLS) and Field Level Security (FLS) should be enabled.
-     * <p>
-     * DLS and FLS are only disabled when the mode is not:
-     * <ul>
-     * <li>{@link OperationMode#PLATINUM}</li>
-     * <li>{@link OperationMode#TRIAL}</li>
-     * </ul>
-     * Note: This does not consider the <em>state</em> of the license so that Security does not suddenly leak information!
-     *
-     * @return {@code true} to enable DLS and FLS. Otherwise {@code false}.
-     */
-    public boolean documentAndFieldLevelSecurityEnabled() {
-        Status status = this.status;
-        return status.getMode() == OperationMode.TRIAL || status.getMode() == OperationMode.PLATINUM;
-    }
-
-    /**
-     * @return the type of realms that are enabled based on the license {@link OperationMode}
-     */
-    public EnabledRealmType enabledRealmType() {
-        OperationMode mode = status.getMode();
-        switch (mode) {
-            case PLATINUM:
-            case TRIAL:
-                return EnabledRealmType.ALL;
-            case GOLD:
-                return EnabledRealmType.DEFAULT;
-            case STANDARD:
-                return EnabledRealmType.NATIVE;
-            default:
-                return EnabledRealmType.NONE;
-        }
-    }
-
-    void updateStatus(Status status) {
-        this.status = status;
-    }
-
-    public enum EnabledRealmType {
-        NONE,
-        NATIVE,
-        DEFAULT,
-        ALL
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java
deleted file mode 100644
index 176d08addc1..00000000000
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java
+++ /dev/null
@@ -1,86 +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.xpack.security;
-
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
-
-/**
- *
- */
-public class SecurityLicensee extends AbstractLicenseeComponent {
-
-    private final SecurityLicenseState securityLicenseState;
-
-    public SecurityLicensee(Settings settings, SecurityLicenseState securityLicenseState) {
-        super(settings, Security.NAME);
-        this.securityLicenseState = securityLicenseState;
-    }
-
-    @Override
-    public void onChange(Status status) {
-        super.onChange(status);
-        securityLicenseState.updateStatus(status);
-    }
-
-    @Override
-    public String[] expirationMessages() {
-        return new String[]{
-                "Cluster health, cluster stats and indices stats operations are blocked",
-                "All data operations (read and write) continue to work"
-        };
-    }
-
-    @Override
-    public String[] acknowledgmentMessages(License.OperationMode currentMode, License.OperationMode newMode) {
-        switch (newMode) {
-            case BASIC:
-                switch (currentMode) {
-                    case TRIAL:
-                    case STANDARD:
-                    case GOLD:
-                    case PLATINUM:
-                        return new String[] {
-                            "The following X-Pack security functionality will be disabled: authentication, authorization, " +
-                            "ip filtering, and auditing. Please restart your node after applying the license.",
-                            "Field and document level access control will be disabled.",
-                            "Custom realms will be ignored."
-                        };
-                }
-                break;
-            case GOLD:
-                switch (currentMode) {
-                    case BASIC:
-                    case STANDARD:
-                    // ^^ though technically it was already disabled, it's not bad to remind them
-                    case TRIAL:
-                    case PLATINUM:
-                        return new String[] {
-                            "Field and document level access control will be disabled.",
-                            "Custom realms will be ignored."
-                        };
-                }
-                break;
-            case STANDARD:
-                switch (currentMode) {
-                    case BASIC:
-                    // ^^ though technically it was already disabled, it's not bad to remind them
-                    case GOLD:
-                    case PLATINUM:
-                    case TRIAL:
-                        return new String[] {
-                                "Authentication will be limited to the native realms.",
-                                "IP filtering and auditing will be disabled.",
-                                "Field and document level access control will be disabled.",
-                                "Custom realms will be ignored."
-                        };
-                }
-        }
-        return Strings.EMPTY_ARRAY;
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java
index 10c0bf70ff1..0d87c1c3260 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java
@@ -33,7 +33,7 @@ import org.elasticsearch.xpack.security.audit.AuditTrail;
 import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
 import org.elasticsearch.xpack.security.authz.privilege.HealthAndStatsPrivilege;
 import org.elasticsearch.xpack.security.crypto.CryptoService;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.threadpool.ThreadPool;
 
@@ -55,13 +55,13 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil
     private final AuditTrail auditTrail;
     private final SecurityActionMapper actionMapper;
     private final Set<RequestInterceptor> requestInterceptors;
-    private final SecurityLicenseState licenseState;
+    private final XPackLicenseState licenseState;
     private final ThreadContext threadContext;
     private final SecurityContext securityContext;
 
     @Inject
     public SecurityActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService,
-                                CryptoService cryptoService, AuditTrailService auditTrail, SecurityLicenseState licenseState,
+                                CryptoService cryptoService, AuditTrailService auditTrail, XPackLicenseState licenseState,
                                 SecurityActionMapper actionMapper, Set<RequestInterceptor> requestInterceptors, ThreadPool threadPool,
                                 SecurityContext securityContext) {
         super(settings);
@@ -83,7 +83,7 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil
          A functional requirement - when the license of security is disabled (invalid/expires), security will continue
          to operate normally, except all read operations will be blocked.
          */
-        if (!licenseState.statsAndHealthEnabled() && LICENSE_EXPIRATION_ACTION_MATCHER.test(action)) {
+        if (licenseState.isStatsAndHealthAllowed() == false && LICENSE_EXPIRATION_ACTION_MATCHER.test(action)) {
             logger.error("blocking [{}] operation due to expired license. Cluster health, cluster stats and indices stats \n" +
                     "operations are blocked on license expiration. All data operations (read and write) continue to work. \n" +
                     "If you have a new license, please update it. Otherwise, please reach out to your support contact.", action);
@@ -95,7 +95,7 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil
         final ThreadContext.StoredContext original = threadContext.newStoredContext();
         final boolean restoreOriginalContext = securityContext.getAuthentication() != null;
         try {
-            if (licenseState.authenticationAndAuthorizationEnabled()) {
+            if (licenseState.isAuthAllowed()) {
                 if (AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, action)) {
                     try (ThreadContext.StoredContext ctx = threadContext.stashContext()) {
                         applyInternal(task, action, request, new SigningListener(this, listener, original), chain);
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java
index 60a8307443d..e78d48ade51 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java
@@ -10,15 +10,13 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.elasticsearch.common.component.AbstractComponent;
-import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.transport.TransportMessage;
 import org.elasticsearch.xpack.security.Security;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
 import org.elasticsearch.xpack.security.authc.AuthenticationToken;
 import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
 import org.elasticsearch.xpack.security.user.User;
@@ -28,7 +26,7 @@ import org.elasticsearch.xpack.security.user.User;
  */
 public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
-    private final SecurityLicenseState securityLicenseState;
+    private final XPackLicenseState licenseState;
     final List<AuditTrail> auditTrails;
 
     @Override
@@ -36,10 +34,10 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
         return "service";
     }
 
-    public AuditTrailService(Settings settings, List<AuditTrail> auditTrails, SecurityLicenseState licenseState) {
+    public AuditTrailService(Settings settings, List<AuditTrail> auditTrails, XPackLicenseState licenseState) {
         super(settings);
         this.auditTrails = Collections.unmodifiableList(auditTrails);
-        this.securityLicenseState = licenseState;
+        this.licenseState = licenseState;
     }
 
     /** Returns the audit trail implementations that this service delegates to. */
@@ -49,7 +47,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void anonymousAccessDenied(String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.anonymousAccessDenied(action, message);
             }
@@ -58,7 +56,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void anonymousAccessDenied(RestRequest request) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.anonymousAccessDenied(request);
             }
@@ -67,7 +65,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void authenticationFailed(RestRequest request) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.authenticationFailed(request);
             }
@@ -76,7 +74,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void authenticationFailed(String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.authenticationFailed(action, message);
             }
@@ -85,7 +83,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.authenticationFailed(token, action, message);
             }
@@ -94,7 +92,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.authenticationFailed(realm, token, action, message);
             }
@@ -103,7 +101,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void authenticationFailed(AuthenticationToken token, RestRequest request) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.authenticationFailed(token, request);
             }
@@ -112,7 +110,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.authenticationFailed(realm, token, request);
             }
@@ -121,7 +119,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void accessGranted(User user, String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.accessGranted(user, action, message);
             }
@@ -130,7 +128,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void accessDenied(User user, String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.accessDenied(user, action, message);
             }
@@ -146,7 +144,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void tamperedRequest(String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.tamperedRequest(action, message);
             }
@@ -155,7 +153,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void tamperedRequest(User user, String action, TransportMessage request) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.tamperedRequest(user, action, request);
             }
@@ -164,7 +162,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.connectionGranted(inetAddress, profile, rule);
             }
@@ -173,7 +171,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.connectionDenied(inetAddress, profile, rule);
             }
@@ -182,7 +180,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void runAsGranted(User user, String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.runAsGranted(user, action, message);
             }
@@ -191,7 +189,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void runAsDenied(User user, String action, TransportMessage message) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.runAsDenied(user, action, message);
             }
@@ -200,7 +198,7 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
 
     @Override
     public void runAsDenied(User user, RestRequest request) {
-        if (securityLicenseState.auditingEnabled()) {
+        if (licenseState.isAuditingAllowed()) {
             for (AuditTrail auditTrail : auditTrails) {
                 auditTrail.runAsDenied(user, request);
             }
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java
index 2fda12f8b42..d54e370a9ae 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java
@@ -5,22 +5,6 @@
  */
 package org.elasticsearch.xpack.security.authc;
 
-import org.elasticsearch.ElasticsearchException;
-import org.elasticsearch.common.component.AbstractLifecycleComponent;
-import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.common.settings.Setting;
-import org.elasticsearch.common.settings.Setting.Property;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.env.Environment;
-import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
-import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
-import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
-import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
-import org.elasticsearch.xpack.security.authc.file.FileRealm;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
-import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
-import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -30,6 +14,21 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.common.component.AbstractLifecycleComponent;
+import org.elasticsearch.common.settings.Setting;
+import org.elasticsearch.common.settings.Setting.Property;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.env.Environment;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState.AllowedRealmType;
+import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
+import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
+import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
+import org.elasticsearch.xpack.security.authc.file.FileRealm;
+import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
+import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
+
 import static org.elasticsearch.xpack.security.Security.setting;
 
 /**
@@ -44,7 +43,7 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
 
     private final Environment env;
     private final Map<String, Realm.Factory> factories;
-    private final SecurityLicenseState securityLicenseState;
+    private final XPackLicenseState licenseState;
     private final ReservedRealm reservedRealm;
 
     protected List<Realm> realms = Collections.emptyList();
@@ -53,12 +52,12 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
     // a list of realms that are considered native, that is they only interact with x-pack and no 3rd party auth sources
     protected List<Realm> nativeRealmsOnly = Collections.emptyList();
 
-    public Realms(Settings settings, Environment env, Map<String, Realm.Factory> factories, SecurityLicenseState securityLicenseState,
+    public Realms(Settings settings, Environment env, Map<String, Realm.Factory> factories, XPackLicenseState licenseState,
                   ReservedRealm reservedRealm) {
         super(settings);
         this.env = env;
         this.factories = factories;
-        this.securityLicenseState = securityLicenseState;
+        this.licenseState = licenseState;
         this.reservedRealm = reservedRealm;
     }
 
@@ -105,12 +104,12 @@ public class Realms extends AbstractLifecycleComponent implements Iterable<Realm
 
     @Override
     public Iterator<Realm> iterator() {
-        if (securityLicenseState.authenticationAndAuthorizationEnabled() == false) {
+        if (licenseState.isAuthAllowed() == false) {
             return Collections.emptyIterator();
         }
 
-        EnabledRealmType enabledRealmType = securityLicenseState.enabledRealmType();
-        switch (enabledRealmType) {
+        AllowedRealmType allowedRealmType = licenseState.allowedRealmType();
+        switch (allowedRealmType) {
             case ALL:
                 return realms.iterator();
             case DEFAULT:
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java
index 65aafbf46a3..4e0c25db6cc 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java
@@ -45,6 +45,7 @@ import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.shard.IndexSearcherWrapper;
 import org.elasticsearch.index.shard.ShardId;
 import org.elasticsearch.index.shard.ShardUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.script.ExecutableScript;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.script.ScriptContext;
@@ -52,7 +53,6 @@ import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.xpack.security.authc.Authentication;
 import org.elasticsearch.xpack.security.authz.AuthorizationService;
 import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
 import org.elasticsearch.xpack.security.support.Exceptions;
 import org.elasticsearch.xpack.security.user.User;
 
@@ -85,14 +85,14 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
     private final Set<String> allowedMetaFields;
     private final QueryShardContext queryShardContext;
     private final BitsetFilterCache bitsetFilterCache;
-    private final SecurityLicenseState securityLicenseState;
+    private final XPackLicenseState licenseState;
     private final ThreadContext threadContext;
     private final ESLogger logger;
     private final ScriptService scriptService;
 
     public SecurityIndexSearcherWrapper(IndexSettings indexSettings, QueryShardContext queryShardContext,
                                         MapperService mapperService, BitsetFilterCache bitsetFilterCache,
-                                        ThreadContext threadContext, SecurityLicenseState securityLicenseState,
+                                        ThreadContext threadContext, XPackLicenseState licenseState,
                                         ScriptService scriptService) {
         this.scriptService = scriptService;
         this.logger = Loggers.getLogger(getClass(), indexSettings.getSettings());
@@ -100,7 +100,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
         this.queryShardContext = queryShardContext;
         this.bitsetFilterCache = bitsetFilterCache;
         this.threadContext = threadContext;
-        this.securityLicenseState = securityLicenseState;
+        this.licenseState = licenseState;
 
         Set<String> allowedMetaFields = new HashSet<>();
         allowedMetaFields.addAll(Arrays.asList(MapperService.getAllMetaFields()));
@@ -114,7 +114,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
 
     @Override
     protected DirectoryReader wrap(DirectoryReader reader) {
-        if (securityLicenseState.documentAndFieldLevelSecurityEnabled() == false) {
+        if (licenseState.isDocumentAndFieldLevelSecurityAllowed() == false) {
             return reader;
         }
 
@@ -171,7 +171,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
 
     @Override
     protected IndexSearcher wrap(IndexSearcher searcher) throws EngineException {
-        if (securityLicenseState.documentAndFieldLevelSecurityEnabled() == false) {
+        if (licenseState.isDocumentAndFieldLevelSecurityAllowed() == false) {
             return searcher;
         }
 
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java
index 9efe72db14e..a3796b60f09 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java
@@ -19,7 +19,7 @@ import org.elasticsearch.rest.RestFilterChain;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.xpack.security.authc.AuthenticationService;
 import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3HttpServerTransport;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.jboss.netty.handler.ssl.SslHandler;
@@ -35,13 +35,13 @@ public class SecurityRestFilter extends RestFilter {
 
     private final AuthenticationService service;
     private final ESLogger logger;
-    private final SecurityLicenseState licenseState;
+    private final XPackLicenseState licenseState;
     private final ThreadContext threadContext;
     private final boolean extractClientCertificate;
 
     @Inject
     public SecurityRestFilter(AuthenticationService service, RestController controller, Settings settings,
-                              ThreadPool threadPool, SecurityLicenseState licenseState) {
+                              ThreadPool threadPool, XPackLicenseState licenseState) {
         this.service = service;
         this.licenseState = licenseState;
         this.threadContext = threadPool.getThreadContext();
@@ -59,7 +59,7 @@ public class SecurityRestFilter extends RestFilter {
     @Override
     public void process(RestRequest request, RestChannel channel, NodeClient client, RestFilterChain filterChain) throws Exception {
 
-        if (licenseState.authenticationAndAuthorizationEnabled()) {
+        if (licenseState.isAuthAllowed()) {
             // CORS - allow for preflight unauthenticated OPTIONS request
             if (request.method() != RestRequest.Method.OPTIONS) {
                 if (extractClientCertificate) {
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/SecurityServerTransportService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/SecurityServerTransportService.java
index 47eb2f99fe8..ea1911b9d90 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/SecurityServerTransportService.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/SecurityServerTransportService.java
@@ -14,7 +14,7 @@ import org.elasticsearch.xpack.security.authc.AuthenticationService;
 import org.elasticsearch.xpack.security.authz.AuthorizationService;
 import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
 import org.elasticsearch.xpack.security.authz.accesscontrol.RequestContext;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.threadpool.ThreadPool;
@@ -46,7 +46,7 @@ public class SecurityServerTransportService extends TransportService {
     protected final AuthorizationService authzService;
     protected final SecurityActionMapper actionMapper;
     protected final ClientTransportFilter clientFilter;
-    protected final SecurityLicenseState licenseState;
+    protected final XPackLicenseState licenseState;
 
     protected final Map<String, ServerTransportFilter> profileFilters;
 
@@ -56,7 +56,7 @@ public class SecurityServerTransportService extends TransportService {
                                           AuthorizationService authzService,
                                           SecurityActionMapper actionMapper,
                                           ClientTransportFilter clientTransportFilter,
-                                          SecurityLicenseState licenseState) {
+                                          XPackLicenseState licenseState) {
         super(settings, transport, threadPool);
         this.authcService = authcService;
         this.authzService = authzService;
@@ -155,11 +155,11 @@ public class SecurityServerTransportService extends TransportService {
         protected final String action;
         protected final TransportRequestHandler<T> handler;
         private final Map<String, ServerTransportFilter> profileFilters;
-        private final SecurityLicenseState licenseState;
+        private final XPackLicenseState licenseState;
         private final ThreadContext threadContext;
 
         public ProfileSecuredRequestHandler(String action, TransportRequestHandler<T> handler,
-                                            Map<String, ServerTransportFilter> profileFilters, SecurityLicenseState licenseState,
+                                            Map<String, ServerTransportFilter> profileFilters, XPackLicenseState licenseState,
                                             ThreadContext threadContext) {
             this.action = action;
             this.handler = handler;
@@ -171,7 +171,7 @@ public class SecurityServerTransportService extends TransportService {
         @Override
         public void messageReceived(T request, TransportChannel channel, Task task) throws Exception {
             try (ThreadContext.StoredContext ctx = threadContext.newStoredContext()) {
-                if (licenseState.authenticationAndAuthorizationEnabled()) {
+                if (licenseState.isAuthAllowed()) {
                     String profile = channel.getProfileName();
                     ServerTransportFilter filter = profileFilters.get(profile);
 
diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/IPFilter.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/IPFilter.java
index 98d977dea9c..dc57434b81c 100644
--- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/IPFilter.java
+++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/IPFilter.java
@@ -17,7 +17,7 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.transport.BoundTransportAddress;
 import org.elasticsearch.common.transport.TransportAddress;
 import org.elasticsearch.xpack.security.audit.AuditTrail;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.transport.TransportSettings;
 import org.elasticsearch.xpack.security.audit.AuditTrailService;
 
@@ -88,8 +88,8 @@ public class IPFilter {
         }
     };
 
-    private final AuditTrail auditTrail;
-    private final SecurityLicenseState licenseState;
+    private final AuditTrailService auditTrail;
+    private final XPackLicenseState licenseState;
     private final boolean alwaysAllowBoundAddresses;
 
     private final ESLogger logger;
@@ -107,7 +107,7 @@ public class IPFilter {
 
     @Inject
     public IPFilter(final Settings settings, AuditTrailService auditTrail, ClusterSettings clusterSettings,
-                    SecurityLicenseState licenseState) {
+                    XPackLicenseState licenseState) {
         this.logger = Loggers.getLogger(getClass(), settings);
         this.auditTrail = auditTrail;
         this.licenseState = licenseState;
@@ -174,7 +174,7 @@ public class IPFilter {
     }
 
     public boolean accept(String profile, InetAddress peerAddress) {
-        if (licenseState.ipFilteringEnabled() == false) {
+        if (licenseState.isIpFilteringAllowed() == false) {
             return true;
         }
 
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java
deleted file mode 100644
index 13e0cd12b43..00000000000
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java
+++ /dev/null
@@ -1,327 +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.integration;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.elasticsearch.ElasticsearchSecurityException;
-import org.elasticsearch.action.ActionRequest;
-import org.elasticsearch.action.ActionResponse;
-import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
-import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
-import org.elasticsearch.action.admin.cluster.stats.ClusterStatsIndices;
-import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
-import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
-import org.elasticsearch.action.index.IndexResponse;
-import org.elasticsearch.client.Client;
-import org.elasticsearch.client.Response;
-import org.elasticsearch.client.ResponseException;
-import org.elasticsearch.client.transport.NoNodeAvailableException;
-import org.elasticsearch.client.transport.TransportClient;
-import org.elasticsearch.cluster.service.ClusterService;
-import org.elasticsearch.common.inject.Module;
-import org.elasticsearch.common.network.NetworkModule;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.util.concurrent.ThreadContext;
-import org.elasticsearch.env.Environment;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.Licensing;
-import org.elasticsearch.license.plugin.core.LicenseService;
-import org.elasticsearch.license.plugin.core.Licensee;
-import org.elasticsearch.plugins.Plugin;
-import org.elasticsearch.rest.RestHandler;
-import org.elasticsearch.rest.RestStatus;
-import org.elasticsearch.test.SecurityIntegTestCase;
-import org.elasticsearch.test.SecuritySettingsSource;
-import org.elasticsearch.transport.Transport;
-import org.elasticsearch.watcher.ResourceWatcherService;
-import org.elasticsearch.xpack.MockNetty3Plugin;
-import org.elasticsearch.xpack.XPackPlugin;
-import org.elasticsearch.xpack.graph.GraphLicensee;
-import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
-import org.elasticsearch.xpack.XPackTransportClient;
-import org.elasticsearch.xpack.security.Security;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
-import org.elasticsearch.xpack.security.SecurityLicensee;
-import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
-import org.elasticsearch.xpack.support.clock.Clock;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
-import org.junit.After;
-
-import static java.util.Collections.emptyList;
-import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
-/**
- *
- */
-public class LicensingTests extends SecurityIntegTestCase {
-    public static final String ROLES =
-            SecuritySettingsSource.DEFAULT_ROLE + ":\n" +
-                    "  cluster: [ all ]\n" +
-                    "  indices:\n" +
-                    "    - names: '*'\n" +
-                    "      privileges: [manage]\n" +
-                    "    - names: '/.*/'\n" +
-                    "      privileges: [write]\n" +
-                    "    - names: 'test'\n" +
-                    "      privileges: [read]\n" +
-                    "    - names: 'test1'\n" +
-                    "      privileges: [read]\n" +
-                    "\n" +
-                    "role_a:\n" +
-                    "  indices:\n" +
-                    "    - names: 'a'\n" +
-                    "      privileges: [all]\n" +
-                    "\n" +
-                    "role_b:\n" +
-                    "  indices:\n" +
-                    "    - names: 'b'\n" +
-                    "      privileges: [all]\n";
-
-    public static final String USERS =
-            SecuritySettingsSource.CONFIG_STANDARD_USER +
-                    "user_a:{plain}passwd\n" +
-                    "user_b:{plain}passwd\n";
-
-    public static final String USERS_ROLES =
-            SecuritySettingsSource.CONFIG_STANDARD_USER_ROLES +
-                    "role_a:user_a,user_b\n" +
-                    "role_b:user_b\n";
-
-    @Override
-    protected String configRoles() {
-        return ROLES;
-    }
-
-    @Override
-    protected String configUsers() {
-        return USERS;
-    }
-
-    @Override
-    protected String configUsersRoles() {
-        return USERS_ROLES;
-    }
-
-    @Override
-    public Settings nodeSettings(int nodeOrdinal) {
-        return Settings.builder().put(super.nodeSettings(nodeOrdinal))
-                .put(NetworkModule.HTTP_ENABLED.getKey(), true)
-                .build();
-    }
-
-    @Override
-    protected Collection<Class<? extends Plugin>> nodePlugins() {
-        ArrayList<Class<? extends Plugin>> plugins = new ArrayList<>(super.nodePlugins());
-        plugins.add(MockNetty3Plugin.class); // for http
-        return plugins;
-    }
-
-    @Override
-    protected Class<? extends XPackPlugin> xpackPluginClass() {
-        return InternalXPackPlugin.class;
-    }
-
-    @After
-    public void resetLicensing() {
-        enableLicensing();
-    }
-
-    public void testEnableDisableBehaviour() throws Exception {
-        IndexResponse indexResponse = index("test", "type", jsonBuilder()
-                .startObject()
-                .field("name", "value")
-                .endObject());
-        assertThat(indexResponse.isCreated(), is(true));
-
-
-        indexResponse = index("test1", "type", jsonBuilder()
-                .startObject()
-                .field("name", "value1")
-                .endObject());
-        assertThat(indexResponse.isCreated(), is(true));
-
-        refresh();
-
-        Client client = internalCluster().transportClient();
-
-        disableLicensing();
-
-        assertElasticsearchSecurityException(() -> client.admin().indices().prepareStats().get());
-        assertElasticsearchSecurityException(() -> client.admin().cluster().prepareClusterStats().get());
-        assertElasticsearchSecurityException(() -> client.admin().cluster().prepareHealth().get());
-        assertElasticsearchSecurityException(() -> client.admin().cluster().prepareNodesStats().get());
-
-        enableLicensing(randomFrom(OperationMode.values()));
-
-        IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().get();
-        assertNoFailures(indicesStatsResponse);
-
-        ClusterStatsResponse clusterStatsNodeResponse = client.admin().cluster().prepareClusterStats().get();
-        assertThat(clusterStatsNodeResponse, notNullValue());
-        ClusterStatsIndices indices = clusterStatsNodeResponse.getIndicesStats();
-        assertThat(indices, notNullValue());
-        assertThat(indices.getIndexCount(), greaterThanOrEqualTo(2));
-
-        ClusterHealthResponse clusterIndexHealth = client.admin().cluster().prepareHealth().get();
-        assertThat(clusterIndexHealth, notNullValue());
-
-        NodesStatsResponse nodeStats = client.admin().cluster().prepareNodesStats().get();
-        assertThat(nodeStats, notNullValue());
-    }
-
-    public void testRestAuthenticationByLicenseType() throws Exception {
-        Response response = getRestClient().performRequest("GET", "/");
-        // the default of the licensing tests is basic
-        assertThat(response.getStatusLine().getStatusCode(), is(200));
-
-        // generate a new license with a mode that enables auth
-        OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM, OperationMode.STANDARD);
-        enableLicensing(mode);
-        try {
-            getRestClient().performRequest("GET", "/");
-            fail("request should have failed");
-        } catch(ResponseException e) {
-            assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
-        }
-    }
-
-    public void testTransportClientAuthenticationByLicenseType() throws Exception {
-        Settings.Builder builder = Settings.builder()
-            .put(internalCluster().transportClient().settings());
-        // remove user info
-        builder.remove(Security.USER_SETTING.getKey());
-        builder.remove(ThreadContext.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER);
-
-        // basic has no auth
-        try (TransportClient client = new XPackTransportClient(builder.build())) {
-            client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
-            assertGreenClusterState(client);
-        }
-
-        // enable a license that enables security
-        OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM, OperationMode.STANDARD);
-        enableLicensing(mode);
-
-        try (TransportClient client = new XPackTransportClient(builder.build())) {
-            client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
-            client.admin().cluster().prepareHealth().get();
-            fail("should not have been able to connect to a node!");
-        } catch (NoNodeAvailableException e) {
-            // expected
-        }
-    }
-
-    private static void assertElasticsearchSecurityException(ThrowingRunnable runnable) {
-        ElasticsearchSecurityException ee = expectThrows(ElasticsearchSecurityException.class, runnable);
-        assertThat(ee.getHeader("es.license.expired.feature"), hasItem(Security.NAME));
-        assertThat(ee.status(), is(RestStatus.FORBIDDEN));
-    }
-
-    public static void disableLicensing() {
-        disableLicensing(OperationMode.BASIC);
-    }
-
-    public static void disableLicensing(OperationMode operationMode) {
-        for (TestLicenseService service : internalCluster().getInstances(TestLicenseService.class)) {
-            service.disable(operationMode);
-        }
-    }
-
-    public static void enableLicensing() {
-        enableLicensing(OperationMode.BASIC);
-    }
-
-    public static void enableLicensing(OperationMode operationMode) {
-        for (TestLicenseService service : internalCluster().getInstances(TestLicenseService.class)) {
-            service.enable(operationMode);
-        }
-    }
-
-    public static class InternalLicensing extends Licensing {
-
-        @Override
-        public Collection<Module> nodeModules() {
-            return Collections.singletonList(b -> b.bind(LicenseService.class).to(TestLicenseService.class));
-        }
-
-        @Override
-        public Collection<Object> createComponents(ClusterService clusterService, Clock clock, Environment environment,
-                                                   ResourceWatcherService resourceWatcherService,
-                                                   SecurityLicenseState securityLicenseState) {
-            SecurityLicensee securityLicensee = new SecurityLicensee(settings, securityLicenseState);
-            WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
-            MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
-            GraphLicensee graphLicensee = new GraphLicensee(settings);
-            TestLicenseService licensesService = new TestLicenseService(settings, environment, resourceWatcherService,
-                    Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee));
-            return Arrays.asList(securityLicensee, licensesService, watcherLicensee, monitoringLicensee,
-                    graphLicensee, securityLicenseState);
-        }
-
-        public InternalLicensing() {
-            super(Settings.EMPTY);
-        }
-
-        @Override
-        public List<ActionHandler<? extends ActionRequest<?>, ? extends ActionResponse>> getActions() {
-            return emptyList();
-        }
-
-        @Override
-        public List<Class<? extends RestHandler>> getRestHandlers() {
-            return emptyList();
-        }
-    }
-
-    public static class InternalXPackPlugin extends XPackPlugin {
-
-        public InternalXPackPlugin(Settings settings) throws IOException {
-            super(settings);
-            licensing = new InternalLicensing();
-        }
-    }
-
-    public static class TestLicenseService extends LicenseService {
-
-        private final List<Licensee> licensees;
-
-        public TestLicenseService(Settings settings, Environment env, ResourceWatcherService resourceWatcherService,
-                                  List<Licensee> licensees) {
-            super(settings, null, null, env, resourceWatcherService, Collections.emptyList());
-            this.licensees = licensees;
-            enable(OperationMode.BASIC);
-        }
-
-        void enable(OperationMode operationMode) {
-            for (Licensee licensee : licensees) {
-                licensee.onChange(new Licensee.Status(operationMode, true));
-            }
-        }
-
-        void disable(OperationMode operationMode) {
-            for (Licensee licensee : licensees) {
-                licensee.onChange(new Licensee.Status(operationMode, false));
-            }
-        }
-
-        @Override
-        protected void doStart() {}
-
-        @Override
-        protected void doStop() {}
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/plugin/core/LicensingTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/plugin/core/LicensingTests.java
new file mode 100644
index 00000000000..a60459d2b76
--- /dev/null
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/plugin/core/LicensingTests.java
@@ -0,0 +1,222 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+
+import org.elasticsearch.ElasticsearchSecurityException;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
+import org.elasticsearch.action.admin.cluster.stats.ClusterStatsIndices;
+import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.ResponseException;
+import org.elasticsearch.client.transport.NoNodeAvailableException;
+import org.elasticsearch.client.transport.TransportClient;
+import org.elasticsearch.common.network.NetworkModule;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.license.core.License;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.test.SecurityIntegTestCase;
+import org.elasticsearch.test.SecuritySettingsSource;
+import org.elasticsearch.transport.Transport;
+import org.elasticsearch.xpack.MockNetty3Plugin;
+import org.elasticsearch.xpack.XPackTransportClient;
+import org.elasticsearch.xpack.security.Security;
+import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
+import org.junit.After;
+import org.junit.Before;
+
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class LicensingTests extends SecurityIntegTestCase {
+    public static final String ROLES =
+        SecuritySettingsSource.DEFAULT_ROLE + ":\n" +
+            "  cluster: [ all ]\n" +
+            "  indices:\n" +
+            "    - names: '*'\n" +
+            "      privileges: [manage]\n" +
+            "    - names: '/.*/'\n" +
+            "      privileges: [write]\n" +
+            "    - names: 'test'\n" +
+            "      privileges: [read]\n" +
+            "    - names: 'test1'\n" +
+            "      privileges: [read]\n" +
+            "\n" +
+            "role_a:\n" +
+            "  indices:\n" +
+            "    - names: 'a'\n" +
+            "      privileges: [all]\n" +
+            "\n" +
+            "role_b:\n" +
+            "  indices:\n" +
+            "    - names: 'b'\n" +
+            "      privileges: [all]\n";
+
+    public static final String USERS =
+        SecuritySettingsSource.CONFIG_STANDARD_USER +
+            "user_a:{plain}passwd\n" +
+            "user_b:{plain}passwd\n";
+
+    public static final String USERS_ROLES =
+        SecuritySettingsSource.CONFIG_STANDARD_USER_ROLES +
+            "role_a:user_a,user_b\n" +
+            "role_b:user_b\n";
+
+    @Override
+    protected String configRoles() {
+        return ROLES;
+    }
+
+    @Override
+    protected String configUsers() {
+        return USERS;
+    }
+
+    @Override
+    protected String configUsersRoles() {
+        return USERS_ROLES;
+    }
+
+    @Override
+    public Settings nodeSettings(int nodeOrdinal) {
+        return Settings.builder().put(super.nodeSettings(nodeOrdinal))
+            .put(NetworkModule.HTTP_ENABLED.getKey(), true)
+            .build();
+    }
+
+    @Override
+    protected Collection<Class<? extends Plugin>> nodePlugins() {
+        ArrayList<Class<? extends Plugin>> plugins = new ArrayList<>(super.nodePlugins());
+        plugins.add(MockNetty3Plugin.class); // for http
+        return plugins;
+    }
+
+    @Before
+    public void resetLicensing() {
+        enableLicensing();
+    }
+
+    public void testEnableDisableBehaviour() throws Exception {
+        IndexResponse indexResponse = index("test", "type", jsonBuilder()
+            .startObject()
+            .field("name", "value")
+            .endObject());
+        assertThat(indexResponse.isCreated(), is(true));
+
+
+        indexResponse = index("test1", "type", jsonBuilder()
+            .startObject()
+            .field("name", "value1")
+            .endObject());
+        assertThat(indexResponse.isCreated(), is(true));
+
+        refresh();
+
+        Client client = internalCluster().transportClient();
+
+        disableLicensing();
+
+        assertElasticsearchSecurityException(() -> client.admin().indices().prepareStats().get());
+        assertElasticsearchSecurityException(() -> client.admin().cluster().prepareClusterStats().get());
+        assertElasticsearchSecurityException(() -> client.admin().cluster().prepareHealth().get());
+        assertElasticsearchSecurityException(() -> client.admin().cluster().prepareNodesStats().get());
+
+        enableLicensing(randomFrom(License.OperationMode.values()));
+
+        IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().get();
+        assertNoFailures(indicesStatsResponse);
+
+        ClusterStatsResponse clusterStatsNodeResponse = client.admin().cluster().prepareClusterStats().get();
+        assertThat(clusterStatsNodeResponse, notNullValue());
+        ClusterStatsIndices indices = clusterStatsNodeResponse.getIndicesStats();
+        assertThat(indices, notNullValue());
+        assertThat(indices.getIndexCount(), greaterThanOrEqualTo(2));
+
+        ClusterHealthResponse clusterIndexHealth = client.admin().cluster().prepareHealth().get();
+        assertThat(clusterIndexHealth, notNullValue());
+
+        NodesStatsResponse nodeStats = client.admin().cluster().prepareNodesStats().get();
+        assertThat(nodeStats, notNullValue());
+    }
+
+    public void testRestAuthenticationByLicenseType() throws Exception {
+        Response response = getRestClient().performRequest("GET", "/");
+        // the default of the licensing tests is basic
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+
+        // generate a new license with a mode that enables auth
+        License.OperationMode mode = randomFrom(License.OperationMode.GOLD, License.OperationMode.TRIAL,
+            License.OperationMode.PLATINUM, License.OperationMode.STANDARD);
+        enableLicensing(mode);
+        ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest("GET", "/"));
+        assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
+    }
+
+    public void testTransportClientAuthenticationByLicenseType() throws Exception {
+        Settings.Builder builder = Settings.builder()
+            .put(internalCluster().transportClient().settings());
+        // remove user info
+        builder.remove(Security.USER_SETTING.getKey());
+        builder.remove(ThreadContext.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER);
+
+        // basic has no auth
+        try (TransportClient client = new XPackTransportClient(builder.build())) {
+            client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
+            assertGreenClusterState(client);
+        }
+
+        // enable a license that enables security
+        License.OperationMode mode = randomFrom(License.OperationMode.GOLD, License.OperationMode.TRIAL,
+            License.OperationMode.PLATINUM, License.OperationMode.STANDARD);
+        enableLicensing(mode);
+
+        try (TransportClient client = new XPackTransportClient(builder.build())) {
+            client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
+            client.admin().cluster().prepareHealth().get();
+            fail("should not have been able to connect to a node!");
+        } catch (NoNodeAvailableException e) {
+            // expected
+        }
+    }
+
+    private static void assertElasticsearchSecurityException(ThrowingRunnable runnable) {
+        ElasticsearchSecurityException ee = expectThrows(ElasticsearchSecurityException.class, runnable);
+        assertThat(ee.getHeader("es.license.expired.feature"), hasItem(Security.NAME));
+        assertThat(ee.status(), is(RestStatus.FORBIDDEN));
+    }
+
+    public static void disableLicensing() {
+        disableLicensing(License.OperationMode.BASIC);
+    }
+
+    public static void disableLicensing(License.OperationMode operationMode) {
+        for (XPackLicenseState licenseState : internalCluster().getInstances(XPackLicenseState.class)) {
+            licenseState.update(operationMode, false);
+        }
+    }
+
+    public static void enableLicensing() {
+        enableLicensing(License.OperationMode.BASIC);
+    }
+
+    public static void enableLicensing(License.OperationMode operationMode) {
+        for (XPackLicenseState licenseState : internalCluster().getInstances(XPackLicenseState.class)) {
+            licenseState.update(operationMode, true);
+        }
+    }
+}
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/plugin/core/XPackLicenseStateTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/plugin/core/XPackLicenseStateTests.java
new file mode 100644
index 00000000000..15b922b86e4
--- /dev/null
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/plugin/core/XPackLicenseStateTests.java
@@ -0,0 +1,255 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.elasticsearch.license.core.License;
+import org.elasticsearch.license.core.License.OperationMode;
+import org.elasticsearch.license.plugin.core.XPackLicenseState.AllowedRealmType;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.monitoring.Monitoring;
+import org.elasticsearch.xpack.security.Security;
+
+import static org.elasticsearch.license.core.License.OperationMode.MISSING;
+import static org.elasticsearch.license.core.License.OperationMode.BASIC;
+import static org.elasticsearch.license.core.License.OperationMode.GOLD;
+import static org.elasticsearch.license.core.License.OperationMode.PLATINUM;
+import static org.elasticsearch.license.core.License.OperationMode.STANDARD;
+import static org.elasticsearch.license.core.License.OperationMode.TRIAL;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for the {@link XPackLicenseState}
+ */
+public class XPackLicenseStateTests extends ESTestCase {
+
+    /** Creates a license state with the given license type and active state, and checks the given method returns expected. */
+    void assertAllowed(OperationMode mode, boolean active, Predicate<XPackLicenseState> predicate, boolean expected) {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(mode, active);
+        assertEquals(expected, predicate.test(licenseState));
+    }
+
+    /**
+     * Checks the ack message going from the  {@code from} license type to {@code to} license type.
+     * TODO: check the actual messages, not just the number of them! This was copied from previous license tests...
+     */
+    void assertAckMesssages(String feature, OperationMode from, OperationMode to, int expectedMessages) {
+        String[] gotMessages = XPackLicenseState.ACKNOWLEDGMENT_MESSAGES.get(feature).apply(from, to);
+        assertEquals(expectedMessages, gotMessages.length);
+    }
+
+    static <T> T randomFrom(T[] values, Predicate<T> filter) {
+        return randomFrom(Arrays.stream(values).filter(filter).collect(Collectors.toList()));
+    }
+
+    static OperationMode randomMode() {
+        return randomFrom(OperationMode.values());
+    }
+
+    static OperationMode randomTrialStandardGoldOrPlatinumMode() {
+        return randomFrom(TRIAL, STANDARD, GOLD, PLATINUM);
+    }
+
+    static OperationMode randomTrialOrPlatinumMode() {
+        return randomFrom(TRIAL, PLATINUM);
+    }
+
+    public void testSecurityDefaults() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(true));
+        assertThat(licenseState.isAuditingAllowed(), is(true));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(true));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(true));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.ALL));
+    }
+
+    public void testSecurityBasic() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(BASIC, true);
+
+        assertThat(licenseState.isAuthAllowed(), is(false));
+        assertThat(licenseState.isIpFilteringAllowed(), is(false));
+        assertThat(licenseState.isAuditingAllowed(), is(false));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(true));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(false));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.NONE));
+    }
+
+    public void testSecurityBasicExpired() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(BASIC, false);
+
+        assertThat(licenseState.isAuthAllowed(), is(false));
+        assertThat(licenseState.isIpFilteringAllowed(), is(false));
+        assertThat(licenseState.isAuditingAllowed(), is(false));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(false));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(false));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.NONE));
+    }
+
+    public void testSecurityStandard() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(STANDARD, true);
+
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(false));
+        assertThat(licenseState.isAuditingAllowed(), is(false));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(true));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(false));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.NATIVE));
+    }
+
+    public void testSecurityStandardExpired() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(STANDARD, false);
+
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(false));
+        assertThat(licenseState.isAuditingAllowed(), is(false));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(false));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(false));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.NATIVE));
+    }
+
+    public void testSecurityGold() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(GOLD, true);
+
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(true));
+        assertThat(licenseState.isAuditingAllowed(), is(true));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(true));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(false));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.DEFAULT));
+    }
+
+    public void testSecurityGoldExpired() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(GOLD, false);
+
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(true));
+        assertThat(licenseState.isAuditingAllowed(), is(true));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(false));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(false));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.DEFAULT));
+    }
+
+    public void testSecurityPlatinum() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(PLATINUM, true);
+
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(true));
+        assertThat(licenseState.isAuditingAllowed(), is(true));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(true));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(true));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.ALL));
+    }
+
+    public void testSecurityPlatinumExpired() {
+        XPackLicenseState licenseState = new XPackLicenseState();
+        licenseState.update(PLATINUM, false);
+
+        assertThat(licenseState.isAuthAllowed(), is(true));
+        assertThat(licenseState.isIpFilteringAllowed(), is(true));
+        assertThat(licenseState.isAuditingAllowed(), is(true));
+        assertThat(licenseState.isStatsAndHealthAllowed(), is(false));
+        assertThat(licenseState.isDocumentAndFieldLevelSecurityAllowed(), is(true));
+        assertThat(licenseState.allowedRealmType(), is(AllowedRealmType.ALL));
+    }
+
+    public void testSecurityAckBasicToNotGoldOrStandard() {
+        OperationMode toMode = randomFrom(OperationMode.values(), mode -> mode != GOLD && mode != STANDARD);
+        assertAckMesssages(Security.NAME, BASIC, toMode, 0);
+    }
+
+    public void testSecurityAckAnyToTrialOrPlatinum() {
+        assertAckMesssages(Security.NAME, randomMode(), randomTrialOrPlatinumMode(), 0);
+    }
+
+    public void testSecurityAckTrialStandardGoldOrPlatinumToBasic() {
+        assertAckMesssages(Security.NAME, randomTrialStandardGoldOrPlatinumMode(), BASIC, 3);
+    }
+
+    public void testSecurityAckAnyToStandard() {
+        OperationMode from = randomFrom(BASIC, GOLD, PLATINUM, TRIAL);
+        assertAckMesssages(Security.NAME, from, STANDARD, 4);
+    }
+
+    public void testSecurityAckBasicStandardTrialOrPlatinumToGold() {
+        OperationMode from = randomFrom(BASIC, PLATINUM, TRIAL, STANDARD);
+        assertAckMesssages(Security.NAME, from, GOLD, 2);
+    }
+
+    public void testMonitoringAckBasicToAny() {
+        assertAckMesssages(Monitoring.NAME, BASIC, randomMode(), 0);
+    }
+
+    public void testMonitoringAckAnyToTrialGoldOrPlatinum() {
+        assertAckMesssages(Monitoring.NAME, randomMode(), randomTrialStandardGoldOrPlatinumMode(), 0);
+    }
+
+    public void testMonitoringAckNotBasicToBasic() {
+        OperationMode from = randomFrom(STANDARD, GOLD, PLATINUM, TRIAL);
+        assertAckMesssages(Monitoring.NAME, from, BASIC, 2);
+    }
+
+    public void testMonitoringAllowed() {
+        assertAllowed(randomMode(), true, XPackLicenseState::isMonitoringAllowed, true);
+        assertAllowed(randomMode(), false, XPackLicenseState::isMonitoringAllowed, false);
+    }
+
+    public void testMonitoringUpdateRetention() {
+        OperationMode mode = randomFrom(STANDARD, GOLD, PLATINUM, TRIAL);
+        assertAllowed(mode, true, XPackLicenseState::isUpdateRetentionAllowed, true);
+        assertAllowed(BASIC, true, XPackLicenseState::isUpdateRetentionAllowed, false);
+        assertAllowed(MISSING, false, XPackLicenseState::isUpdateRetentionAllowed, false);
+    }
+
+    public void testWatcherPlatinumGoldTrial() throws Exception {
+        assertAllowed(randomFrom(TRIAL, GOLD, PLATINUM), true, XPackLicenseState::isWatcherAllowed, true);
+    }
+
+    public void testWatcherBasicStandardLicense() throws Exception {
+        assertAllowed(randomFrom(BASIC, STANDARD), true, XPackLicenseState::isWatcherAllowed, false);
+    }
+
+    public void testWatcherInactive() {
+        assertAllowed(randomFrom(BASIC, STANDARD), false, XPackLicenseState::isWatcherAllowed, false);
+    }
+
+    public void testWatcherInactivePlatinumGoldTrial() throws Exception {
+        assertAllowed(randomFrom(TRIAL, GOLD, PLATINUM), false, XPackLicenseState::isWatcherAllowed, false);
+    }
+
+    public void testGraphPlatinumTrial() throws Exception {
+        assertAllowed(TRIAL, true, XPackLicenseState::isGraphAllowed, true);
+        assertAllowed(PLATINUM, true, XPackLicenseState::isGraphAllowed, true);
+    }
+
+    public void testGraphBasic() throws Exception {
+        assertAllowed(BASIC, true, XPackLicenseState::isGraphAllowed, false);
+    }
+
+    public void testGraphStandard() throws Exception {
+        assertAllowed(STANDARD, true, XPackLicenseState::isGraphAllowed, false);
+    }
+
+    public void testGraphInactiveBasic() {
+        assertAllowed(BASIC, false, XPackLicenseState::isGraphAllowed, false);
+    }
+
+    public void testGraphInactivePlatinumTrial() throws Exception {
+        assertAllowed(TRIAL, false, XPackLicenseState::isGraphAllowed, false);
+        assertAllowed(PLATINUM, false, XPackLicenseState::isGraphAllowed, false);
+    }
+}
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java
index 7d12adae599..bd266827d73 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java
@@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security;
 import org.elasticsearch.common.collect.MapBuilder;
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.XPackFeatureSet;
 import org.elasticsearch.xpack.security.audit.AuditTrailService;
@@ -41,7 +42,7 @@ import static org.mockito.Mockito.when;
 public class SecurityFeatureSetTests extends ESTestCase {
 
     private Settings settings;
-    private SecurityLicenseState licenseState;
+    private XPackLicenseState licenseState;
     private Realms realms;
     private NamedWriteableRegistry namedWriteableRegistry;
     private IPFilter ipFilter;
@@ -52,7 +53,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
     @Before
     public void init() throws Exception {
         settings = Settings.builder().put("path.home", createTempDir()).build();
-        licenseState = mock(SecurityLicenseState.class);
+        licenseState = mock(XPackLicenseState.class);
         realms = mock(Realms.class);
         namedWriteableRegistry = mock(NamedWriteableRegistry.class);
         ipFilter = mock(IPFilter.class);
@@ -70,7 +71,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
         SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, namedWriteableRegistry, rolesStore,
                 ipFilter, auditTrail, cryptoService);
         boolean available = randomBoolean();
-        when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(available);
+        when(licenseState.isAuthAllowed()).thenReturn(available);
         assertThat(featureSet.available(), is(available));
     }
 
@@ -106,7 +107,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
     public void testUsage() throws Exception {
 
         boolean authcAuthzAvailable = randomBoolean();
-        when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(authcAuthzAvailable);
+        when(licenseState.isAuthAllowed()).thenReturn(authcAuthzAvailable);
 
         Settings.Builder settings = Settings.builder().put(this.settings);
 
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityLicenseStateTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityLicenseStateTests.java
deleted file mode 100644
index 1c05c4155b1..00000000000
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityLicenseStateTests.java
+++ /dev/null
@@ -1,126 +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.xpack.security;
-
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.Licensee;
-import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
-import org.elasticsearch.test.ESTestCase;
-
-import static org.hamcrest.Matchers.is;
-
-/**
- * Unit tests for the {@link SecurityLicenseState}
- */
-public class SecurityLicenseStateTests extends ESTestCase {
-
-    public void testDefaults() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(true));
-        assertThat(licenseState.auditingEnabled(), is(true));
-        assertThat(licenseState.statsAndHealthEnabled(), is(true));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(true));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.ALL));
-    }
-
-    public void testBasic() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, true));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(false));
-        assertThat(licenseState.ipFilteringEnabled(), is(false));
-        assertThat(licenseState.auditingEnabled(), is(false));
-        assertThat(licenseState.statsAndHealthEnabled(), is(true));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.NONE));
-    }
-
-    public void testBasicExpired() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, false));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(false));
-        assertThat(licenseState.ipFilteringEnabled(), is(false));
-        assertThat(licenseState.auditingEnabled(), is(false));
-        assertThat(licenseState.statsAndHealthEnabled(), is(false));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.NONE));
-    }
-
-    public void testStandard() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(OperationMode.STANDARD, true));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(false));
-        assertThat(licenseState.auditingEnabled(), is(false));
-        assertThat(licenseState.statsAndHealthEnabled(), is(true));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.NATIVE));
-    }
-
-    public void testStandardExpired() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(OperationMode.STANDARD, false));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(false));
-        assertThat(licenseState.auditingEnabled(), is(false));
-        assertThat(licenseState.statsAndHealthEnabled(), is(false));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.NATIVE));
-    }
-
-    public void testGold() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, true));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(true));
-        assertThat(licenseState.auditingEnabled(), is(true));
-        assertThat(licenseState.statsAndHealthEnabled(), is(true));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.DEFAULT));
-    }
-
-    public void testGoldExpired() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, false));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(true));
-        assertThat(licenseState.auditingEnabled(), is(true));
-        assertThat(licenseState.statsAndHealthEnabled(), is(false));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.DEFAULT));
-    }
-
-    public void testPlatinum() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, true));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(true));
-        assertThat(licenseState.auditingEnabled(), is(true));
-        assertThat(licenseState.statsAndHealthEnabled(), is(true));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(true));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.ALL));
-    }
-
-    public void testPlatinumExpired() {
-        SecurityLicenseState licenseState = new SecurityLicenseState();
-        licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, false));
-
-        assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
-        assertThat(licenseState.ipFilteringEnabled(), is(true));
-        assertThat(licenseState.auditingEnabled(), is(true));
-        assertThat(licenseState.statsAndHealthEnabled(), is(false));
-        assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(true));
-        assertThat(licenseState.enabledRealmType(), is(EnabledRealmType.ALL));
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityLicenseeTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityLicenseeTests.java
deleted file mode 100644
index 34b399548f3..00000000000
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityLicenseeTests.java
+++ /dev/null
@@ -1,80 +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.xpack.security;
-
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeTestCase;
-import org.elasticsearch.license.plugin.core.Licensee.Status;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-/**
- * Tests {@link SecurityLicensee}.
- * <p>
- * If you change the behavior of these tests, then it means that licensing changes for Security!
- */
-public class SecurityLicenseeTests extends AbstractLicenseeTestCase {
-    private final SecurityLicenseState securityLicenseState = mock(SecurityLicenseState.class);
-
-    public void testOnChangeModifiesSecurityLicenseState() {
-        Status status = mock(Status.class);
-
-        SecurityLicensee licensee = new SecurityLicensee(Settings.EMPTY, securityLicenseState);
-
-        licensee.onChange(status);
-
-        assertSame(status, licensee.getStatus());
-
-        verify(securityLicenseState).updateStatus(status);
-        verifyNoMoreInteractions(securityLicenseState);
-    }
-
-    public void testAcknowledgementMessagesFromBasicToAnyNotGoldOrStandardIsNoOp() {
-        assertEmptyAck(OperationMode.BASIC,
-                randomFrom(OperationMode.values(), mode -> mode != OperationMode.GOLD && mode != OperationMode.STANDARD),
-                this::buildLicensee);
-    }
-
-    public void testAcknowledgementMessagesFromAnyToTrialOrPlatinumIsNoOp() {
-        assertEmptyAck(randomMode(), randomTrialOrPlatinumMode(), this::buildLicensee);
-    }
-
-    public void testAcknowledgementMessagesFromTrialStandardGoldOrPlatinumToBasicNotesLimits() {
-        OperationMode from = randomTrialStandardGoldOrPlatinumMode();
-        OperationMode to = OperationMode.BASIC;
-
-        String[] messages = ackLicenseChange(from, to, this::buildLicensee);
-
-        // leaving messages up to inspection
-        assertThat(fromToMessage(from, to), messages.length, equalTo(3));
-    }
-
-    public void testAcknowlegmentMessagesFromAnyToStandardNotesLimits() {
-        OperationMode from = randomFrom(OperationMode.BASIC, OperationMode.GOLD, OperationMode.PLATINUM, OperationMode.TRIAL);
-        OperationMode to = OperationMode.STANDARD;
-
-        String[] messages = ackLicenseChange(from, to, this::buildLicensee);
-
-        // leaving messages up to inspection
-        assertThat(fromToMessage(from, to), messages.length, equalTo(4));
-    }
-
-    public void testAcknowledgementMessagesFromBasicStandardTrialOrPlatinumToGoldNotesLimits() {
-        OperationMode from = randomFrom(OperationMode.BASIC, OperationMode.PLATINUM, OperationMode.TRIAL, OperationMode.STANDARD);
-        String[] messages = ackLicenseChange(from, OperationMode.GOLD, this::buildLicensee);
-
-        // leaving messages up to inspection
-        assertThat(messages.length, equalTo(2));
-    }
-
-    private SecurityLicensee buildLicensee() {
-        return new SecurityLicensee(Settings.EMPTY, securityLicenseState);
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginEnabledDisabledTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginEnabledDisabledTests.java
deleted file mode 100644
index a38a93d98bd..00000000000
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityPluginEnabledDisabledTests.java
+++ /dev/null
@@ -1,79 +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.xpack.security;
-
-import org.elasticsearch.common.network.NetworkModule;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.integration.LicensingTests;
-import org.elasticsearch.test.SecurityIntegTestCase;
-import org.elasticsearch.transport.Transport;
-import org.elasticsearch.transport.TransportService;
-import org.elasticsearch.xpack.XPackPlugin;
-import org.elasticsearch.xpack.security.transport.SecurityServerTransportService;
-import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
-import org.hamcrest.Matcher;
-import org.junit.After;
-import org.junit.BeforeClass;
-
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.not;
-
-public class SecurityPluginEnabledDisabledTests extends SecurityIntegTestCase {
-    private static boolean enabled;
-
-    @BeforeClass
-    public static void init() {
-        enabled = randomBoolean();
-    }
-
-    @After
-    public void cleanup() throws Exception {
-        // now that on a disabled license we block cluster health/stats and indices stats, we need
-        // to make sure that after the tests (which disable the license for testing purposes) we
-        // reenabled the license, so the internal cluster will be cleaned appropriately.
-        logger.info("cleanup: enabling licensing...");
-        LicensingTests.enableLicensing();
-    }
-    @Override
-    protected Class<? extends XPackPlugin> xpackPluginClass() {
-        return LicensingTests.InternalXPackPlugin.class;
-    }
-
-    @Override
-    protected Settings nodeSettings(int nodeOrdinal) {
-        logger.info("******* security is {}", enabled ? "enabled" : "disabled");
-        return Settings.builder()
-                .put(super.nodeSettings(nodeOrdinal))
-                .put(XPackPlugin.featureEnabledSetting(Security.NAME), enabled)
-                .put(NetworkModule.HTTP_ENABLED.getKey(), true)
-                .build();
-    }
-
-    @Override
-    protected Settings transportClientSettings() {
-        return Settings.builder()
-                .put(super.transportClientSettings())
-                .put(XPackPlugin.featureEnabledSetting(Security.NAME), enabled)
-                .build();
-    }
-
-    public void testTransportEnabledDisabled() throws Exception {
-        for (TransportService service : internalCluster().getInstances(TransportService.class)) {
-            Matcher<TransportService> matcher = instanceOf(SecurityServerTransportService.class);
-            if (!enabled) {
-                matcher = not(matcher);
-            }
-            assertThat(service, matcher);
-        }
-        for (Transport transport : internalCluster().getInstances(Transport.class)) {
-            Matcher<Transport> matcher = instanceOf(SecurityNetty3Transport.class);
-            if (!enabled) {
-                matcher = not(matcher);
-            }
-            assertThat(transport, matcher);
-        }
-    }
-}
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java
index 5cfe68781ac..63e83c52713 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java
@@ -14,6 +14,7 @@ import java.util.Map;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.env.Environment;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.xpack.extensions.XPackExtension;
@@ -52,7 +53,7 @@ public class SecurityTests extends ESTestCase {
         Settings settings = Settings.builder().put(testSettings)
             .put("path.home", createTempDir()).build();
         Environment env = new Environment(settings);
-        Security security = new Security(settings, env);
+        Security security = new Security(settings, env, new XPackLicenseState());
         ThreadPool threadPool = mock(ThreadPool.class);
         ClusterService clusterService = mock(ClusterService.class);
         return security.createComponents(null, threadPool, clusterService, null, Arrays.asList(extensions));
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/VersionCompatibilityTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/VersionCompatibilityTests.java
index e262b646a98..89bbe0df04d 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/VersionCompatibilityTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/VersionCompatibilityTests.java
@@ -27,7 +27,7 @@ import static org.hamcrest.CoreMatchers.is;
 public class VersionCompatibilityTests extends ESTestCase {
     public void testCompatibility() {
         /**
-         * see https://github.com/elasticsearch/elasticsearch/issues/9372 {@link SecurityLicensee}
+         * see https://github.com/elasticsearch/elasticsearch/issues/9372 {@link org.elasticsearch.license.plugin.core.XPackLicenseState}
          * Once es core supports merging cluster level custom metadata (licenses in our case), the tribe node will see some license
          * coming from the tribe and everything will be ok.
          *
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java
index cd5c71dad71..7ba58060678 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java
@@ -18,7 +18,6 @@ import org.elasticsearch.tasks.Task;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.xpack.security.SecurityContext;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
 import org.elasticsearch.xpack.security.action.SecurityActionMapper;
 import org.elasticsearch.xpack.security.audit.AuditTrailService;
 import org.elasticsearch.xpack.security.authc.Authentication;
@@ -28,6 +27,7 @@ import org.elasticsearch.xpack.security.authz.AuthorizationService;
 import org.elasticsearch.xpack.security.user.SystemUser;
 import org.elasticsearch.xpack.security.user.User;
 import org.elasticsearch.xpack.security.crypto.CryptoService;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.junit.Before;
 
 import static org.hamcrest.Matchers.equalTo;
@@ -50,7 +50,7 @@ public class SecurityActionFilterTests extends ESTestCase {
     private AuthorizationService authzService;
     private CryptoService cryptoService;
     private AuditTrailService auditTrail;
-    private SecurityLicenseState securityLicenseState;
+    private XPackLicenseState licenseState;
     private SecurityActionFilter filter;
 
     @Before
@@ -59,12 +59,12 @@ public class SecurityActionFilterTests extends ESTestCase {
         authzService = mock(AuthorizationService.class);
         cryptoService = mock(CryptoService.class);
         auditTrail = mock(AuditTrailService.class);
-        securityLicenseState = mock(SecurityLicenseState.class);
-        when(securityLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(true);
-        when(securityLicenseState.statsAndHealthEnabled()).thenReturn(true);
+        licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isAuthAllowed()).thenReturn(true);
+        when(licenseState.isStatsAndHealthAllowed()).thenReturn(true);
         ThreadPool threadPool = mock(ThreadPool.class);
         when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY));
-        filter = new SecurityActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail, securityLicenseState,
+        filter = new SecurityActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail, licenseState,
                 new SecurityActionMapper(), new HashSet<>(), threadPool, mock(SecurityContext.class));
     }
 
@@ -135,7 +135,7 @@ public class SecurityActionFilterTests extends ESTestCase {
         ActionListener listener = mock(ActionListener.class);
         ActionFilterChain chain = mock(ActionFilterChain.class);
         Task task = mock(Task.class);
-        when(securityLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(false);
+        when(licenseState.isAuthAllowed()).thenReturn(false);
         filter.apply(task, "_action", request, listener, chain);
         verifyZeroInteractions(authcService);
         verifyZeroInteractions(authzService);
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java
index 27b225d3c42..514c29e9aa3 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java
@@ -7,7 +7,7 @@ package org.elasticsearch.xpack.security.audit;
 
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.rest.RestRequest;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.security.user.User;
 import org.elasticsearch.xpack.security.authc.AuthenticationToken;
 import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@@ -38,8 +38,8 @@ public class AuditTrailServiceTests extends ESTestCase {
     private AuthenticationToken token;
     private TransportMessage message;
     private RestRequest restRequest;
-    private SecurityLicenseState securityLicenseState;
-    private boolean auditingEnabled;
+    private XPackLicenseState licenseState;
+    private boolean isAuditingAllowed;
 
     @Before
     public void init() throws Exception {
@@ -48,10 +48,10 @@ public class AuditTrailServiceTests extends ESTestCase {
             auditTrailsBuilder.add(mock(AuditTrail.class));
         }
         auditTrails = unmodifiableList(auditTrailsBuilder);
-        securityLicenseState = mock(SecurityLicenseState.class);
-        service = new AuditTrailService(Settings.EMPTY, auditTrails, securityLicenseState);
-        auditingEnabled = randomBoolean();
-        when(securityLicenseState.auditingEnabled()).thenReturn(auditingEnabled);
+        licenseState = mock(XPackLicenseState.class);
+        service = new AuditTrailService(Settings.EMPTY, auditTrails, licenseState);
+        isAuditingAllowed = randomBoolean();
+        when(licenseState.isAuditingAllowed()).thenReturn(isAuditingAllowed);
         token = mock(AuthenticationToken.class);
         message = mock(TransportMessage.class);
         restRequest = mock(RestRequest.class);
@@ -59,8 +59,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAuthenticationFailed() throws Exception {
         service.authenticationFailed(token, "_action", message);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).authenticationFailed(token, "_action", message);
             }
@@ -71,8 +71,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAuthenticationFailedNoToken() throws Exception {
         service.authenticationFailed("_action", message);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).authenticationFailed("_action", message);
             }
@@ -83,8 +83,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAuthenticationFailedRestNoToken() throws Exception {
         service.authenticationFailed(restRequest);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).authenticationFailed(restRequest);
             }
@@ -95,8 +95,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAuthenticationFailedRest() throws Exception {
         service.authenticationFailed(token, restRequest);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).authenticationFailed(token, restRequest);
             }
@@ -107,8 +107,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAuthenticationFailedRealm() throws Exception {
         service.authenticationFailed("_realm", token, "_action", message);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).authenticationFailed("_realm", token, "_action", message);
             }
@@ -119,8 +119,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAuthenticationFailedRestRealm() throws Exception {
         service.authenticationFailed("_realm", token, restRequest);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).authenticationFailed("_realm", token, restRequest);
             }
@@ -131,8 +131,8 @@ public class AuditTrailServiceTests extends ESTestCase {
 
     public void testAnonymousAccess() throws Exception {
         service.anonymousAccessDenied("_action", message);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).anonymousAccessDenied("_action", message);
             }
@@ -144,8 +144,8 @@ public class AuditTrailServiceTests extends ESTestCase {
     public void testAccessGranted() throws Exception {
         User user = new User("_username", "r1");
         service.accessGranted(user, "_action", message);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).accessGranted(user, "_action", message);
             }
@@ -157,8 +157,8 @@ public class AuditTrailServiceTests extends ESTestCase {
     public void testAccessDenied() throws Exception {
         User user = new User("_username", "r1");
         service.accessDenied(user, "_action", message);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).accessDenied(user, "_action", message);
             }
@@ -171,8 +171,8 @@ public class AuditTrailServiceTests extends ESTestCase {
         InetAddress inetAddress = InetAddress.getLoopbackAddress();
         SecurityIpFilterRule rule = randomBoolean() ? SecurityIpFilterRule.ACCEPT_ALL : IPFilter.DEFAULT_PROFILE_ACCEPT_ALL;
         service.connectionGranted(inetAddress, "client", rule);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).connectionGranted(inetAddress, "client", rule);
             }
@@ -185,8 +185,8 @@ public class AuditTrailServiceTests extends ESTestCase {
         InetAddress inetAddress = InetAddress.getLoopbackAddress();
         SecurityIpFilterRule rule = new SecurityIpFilterRule(false, "_all");
         service.connectionDenied(inetAddress, "client", rule);
-        verify(securityLicenseState).auditingEnabled();
-        if (auditingEnabled) {
+        verify(licenseState).isAuditingAllowed();
+        if (isAuditingAllowed) {
             for (AuditTrail auditTrail : auditTrails) {
                 verify(auditTrail).connectionDenied(inetAddress, "client", rule);
             }
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java
index b53277f96b3..1a73410842d 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java
@@ -16,13 +16,12 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.env.Environment;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.rest.FakeRestRequest;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportMessage;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
-import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
 import org.elasticsearch.xpack.security.audit.AuditTrailService;
 import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
 import org.elasticsearch.xpack.security.authc.AuthenticationService.Authenticator;
@@ -88,11 +87,11 @@ public class AuthenticationServiceTests extends ESTestCase {
                 .put("path.home", createTempDir())
                 .put("node.name", "authc_test")
                 .build();
-        SecurityLicenseState securityLicenseState = mock(SecurityLicenseState.class);
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.ALL);
-        when(securityLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(true);
-        realms = new Realms(Settings.EMPTY, new Environment(settings), Collections.<String, Realm.Factory>emptyMap(), securityLicenseState,
-                mock(ReservedRealm.class)) {
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.allowedRealmType()).thenReturn(XPackLicenseState.AllowedRealmType.ALL);
+        when(licenseState.isAuthAllowed()).thenReturn(true);
+        realms = new Realms(Settings.EMPTY, new Environment(settings), Collections.<String, Realm.Factory>emptyMap(),
+            licenseState, mock(ReservedRealm.class)) {
 
             @Override
             protected void doStart() {
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java
index 2102a461789..81faa8cc74b 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/RealmsTests.java
@@ -8,13 +8,13 @@ package org.elasticsearch.xpack.security.authc;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.env.Environment;
-import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
 import org.elasticsearch.xpack.security.user.User;
 import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
 import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
 import org.elasticsearch.xpack.security.authc.file.FileRealm;
 import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState.AllowedRealmType;
 import org.elasticsearch.test.ESTestCase;
 import org.junit.Before;
 
@@ -35,7 +35,7 @@ import static org.mockito.Mockito.when;
 
 public class RealmsTests extends ESTestCase {
     private Map<String, Realm.Factory> factories;
-    private SecurityLicenseState securityLicenseState;
+    private XPackLicenseState licenseState;
     private ReservedRealm reservedRealm;
 
     @Before
@@ -47,10 +47,10 @@ public class RealmsTests extends ESTestCase {
             String name = "type_" + i;
             factories.put(name, config -> new DummyRealm(name, config));
         }
-        securityLicenseState = mock(SecurityLicenseState.class);
+        licenseState = mock(XPackLicenseState.class);
         reservedRealm = mock(ReservedRealm.class);
-        when(securityLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(true);
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.ALL);
+        when(licenseState.isAuthAllowed()).thenReturn(true);
+        when(licenseState.allowedRealmType()).thenReturn(AllowedRealmType.ALL);
     }
 
     public void testWithSettings() throws Exception {
@@ -69,7 +69,7 @@ public class RealmsTests extends ESTestCase {
         }
         Settings settings = builder.build();
         Environment env = new Environment(settings);
-        Realms realms = new Realms(settings, env, factories, securityLicenseState, reservedRealm);
+        Realms realms = new Realms(settings, env, factories, licenseState, reservedRealm);
         realms.start();
 
         Iterator<Realm> iterator = realms.iterator();
@@ -98,7 +98,7 @@ public class RealmsTests extends ESTestCase {
                 .build();
         Environment env = new Environment(settings);
         try {
-            new Realms(settings, env, factories, securityLicenseState, reservedRealm).start();
+            new Realms(settings, env, factories, licenseState, reservedRealm).start();
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException e) {
             assertThat(e.getMessage(), containsString("multiple [file] realms are configured"));
@@ -107,7 +107,7 @@ public class RealmsTests extends ESTestCase {
 
     public void testWithEmptySettings() throws Exception {
         Realms realms = new Realms(Settings.EMPTY, new Environment(Settings.builder().put("path.home", createTempDir()).build()),
-                factories, securityLicenseState, reservedRealm);
+                factories, licenseState, reservedRealm);
         realms.start();
         Iterator<Realm> iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
@@ -140,7 +140,7 @@ public class RealmsTests extends ESTestCase {
         }
         Settings settings = builder.build();
         Environment env = new Environment(settings);
-        Realms realms = new Realms(settings, env, factories, securityLicenseState, reservedRealm);
+        Realms realms = new Realms(settings, env, factories, licenseState, reservedRealm);
         realms.start();
 
         // this is the iterator when licensed
@@ -158,7 +158,7 @@ public class RealmsTests extends ESTestCase {
             i++;
         }
 
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.DEFAULT);
+        when(licenseState.allowedRealmType()).thenReturn(AllowedRealmType.DEFAULT);
 
         iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
@@ -174,7 +174,7 @@ public class RealmsTests extends ESTestCase {
         assertThat(realm.name(), equalTo("default_" + NativeRealm.TYPE));
         assertThat(iter.hasNext(), is(false));
 
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.NATIVE);
+        when(licenseState.allowedRealmType()).thenReturn(AllowedRealmType.NATIVE);
 
         iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
@@ -202,7 +202,7 @@ public class RealmsTests extends ESTestCase {
                 .put("xpack.security.authc.realms.custom.order", "1");
         Settings settings = builder.build();
         Environment env = new Environment(settings);
-        Realms realms = new Realms(settings, env, factories, securityLicenseState, reservedRealm);
+        Realms realms = new Realms(settings, env, factories, licenseState, reservedRealm);
         realms.start();
         Iterator<Realm> iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
@@ -219,7 +219,7 @@ public class RealmsTests extends ESTestCase {
         }
         assertThat(types, contains("ldap", "type_0"));
 
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.DEFAULT);
+        when(licenseState.allowedRealmType()).thenReturn(AllowedRealmType.DEFAULT);
         iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
         realm = iter.next();
@@ -232,7 +232,7 @@ public class RealmsTests extends ESTestCase {
         }
         assertThat(i, is(1));
 
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.NATIVE);
+        when(licenseState.allowedRealmType()).thenReturn(AllowedRealmType.NATIVE);
         iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
         realm = iter.next();
@@ -259,7 +259,7 @@ public class RealmsTests extends ESTestCase {
                 .put("xpack.security.authc.realms.native.order", "1");
         Settings settings = builder.build();
         Environment env = new Environment(settings);
-        Realms realms = new Realms(settings, env, factories, securityLicenseState, reservedRealm);
+        Realms realms = new Realms(settings, env, factories, licenseState, reservedRealm);
         realms.start();
         Iterator<Realm> iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
@@ -273,7 +273,7 @@ public class RealmsTests extends ESTestCase {
         assertThat(realm.type(), is(type));
         assertThat(iter.hasNext(), is(false));
 
-        when(securityLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.NATIVE);
+        when(licenseState.allowedRealmType()).thenReturn(AllowedRealmType.NATIVE);
         iter = realms.iterator();
         assertThat(iter.hasNext(), is(true));
         realm = iter.next();
@@ -305,7 +305,7 @@ public class RealmsTests extends ESTestCase {
         }
         Settings settings = builder.build();
         Environment env = new Environment(settings);
-        Realms realms = new Realms(settings, env, factories, securityLicenseState, reservedRealm);
+        Realms realms = new Realms(settings, env, factories, licenseState, reservedRealm);
         realms.start();
         Iterator<Realm> iterator = realms.iterator();
         Realm realm = iterator.next();
@@ -343,12 +343,12 @@ public class RealmsTests extends ESTestCase {
                 .put("xpack.security.authc.realms.realm_1.order", 0)
                 .build();
         Environment env = new Environment(settings);
-        Realms realms = new Realms(settings, env, factories, securityLicenseState, reservedRealm);
+        Realms realms = new Realms(settings, env, factories, licenseState, reservedRealm);
         realms.start();
 
         assertThat(realms.iterator().hasNext(), is(true));
 
-        when(securityLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(false);
+        when(licenseState.isAuthAllowed()).thenReturn(false);
         assertThat(realms.iterator().hasNext(), is(false));
     }
 
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperIntegrationTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperIntegrationTests.java
index b2ba7b49429..51a836dad05 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperIntegrationTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperIntegrationTests.java
@@ -34,8 +34,8 @@ import org.elasticsearch.index.query.QueryParseContext;
 import org.elasticsearch.index.query.QueryShardContext;
 import org.elasticsearch.index.query.TermQueryBuilder;
 import org.elasticsearch.index.shard.ShardId;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.script.ScriptService;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.IndexSettingsModule;
 
@@ -78,8 +78,8 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
 
             }
         });
-        SecurityLicenseState licenseState = mock(SecurityLicenseState.class);
-        when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(true);
         SecurityIndexSearcherWrapper wrapper = new SecurityIndexSearcherWrapper(indexSettings, queryShardContext, mapperService,
                 bitsetFilterCache, threadContext, licenseState, scriptService) {
 
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperUnitTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperUnitTests.java
index 928c3db2ce3..3e029b98141 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperUnitTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SecurityIndexSearcherWrapperUnitTests.java
@@ -63,7 +63,7 @@ import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.search.aggregations.LeafBucketCollector;
 import org.elasticsearch.watcher.ResourceWatcherService;
 import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.IndexSettingsModule;
 import org.elasticsearch.xpack.security.user.User;
@@ -104,7 +104,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
     private ScriptService scriptService;
     private SecurityIndexSearcherWrapper securityIndexSearcherWrapper;
     private ElasticsearchDirectoryReader esIn;
-    private SecurityLicenseState licenseState;
+    private XPackLicenseState licenseState;
     private IndexSettings indexSettings;
 
     @Before
@@ -119,8 +119,8 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
                 new IndicesModule(new NamedWriteableRegistry(), emptyList()).getMapperRegistry(), () -> null);
 
         ShardId shardId = new ShardId(index, 0);
-        licenseState = mock(SecurityLicenseState.class);
-        when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
+        licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(true);
         threadContext = new ThreadContext(Settings.EMPTY);
         IndexShard indexShard = mock(IndexShard.class);
         when(indexShard.shardId()).thenReturn(shardId);
@@ -175,7 +175,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
     }
 
     public void testWrapReaderWhenFeatureDisabled() throws Exception {
-        when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(false);
+        when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(false);
         securityIndexSearcherWrapper =
                 new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
         DirectoryReader reader = securityIndexSearcherWrapper.wrap(esIn);
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/SecurityRestFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/SecurityRestFilterTests.java
index badc05f9d2c..c9f8d291aa6 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/SecurityRestFilterTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/rest/SecurityRestFilterTests.java
@@ -13,10 +13,10 @@ import org.elasticsearch.rest.RestController;
 import org.elasticsearch.rest.RestFilterChain;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.xpack.security.authc.Authentication;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.xpack.security.authc.AuthenticationService;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.threadpool.ThreadPool;
-import org.elasticsearch.xpack.security.authc.AuthenticationService;
 import org.junit.Before;
 
 import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
@@ -34,7 +34,7 @@ public class SecurityRestFilterTests extends ESTestCase {
     private RestChannel channel;
     private RestFilterChain chain;
     private SecurityRestFilter filter;
-    private SecurityLicenseState licenseState;
+    private XPackLicenseState licenseState;
 
     @Before
     public void init() throws Exception {
@@ -42,8 +42,8 @@ public class SecurityRestFilterTests extends ESTestCase {
         RestController restController = mock(RestController.class);
         channel = mock(RestChannel.class);
         chain = mock(RestFilterChain.class);
-        licenseState = mock(SecurityLicenseState.class);
-        when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(true);
+        licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isAuthAllowed()).thenReturn(true);
         ThreadPool threadPool = mock(ThreadPool.class);
         when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY));
         filter = new SecurityRestFilter(authcService, restController, Settings.EMPTY, threadPool, licenseState);
@@ -61,7 +61,7 @@ public class SecurityRestFilterTests extends ESTestCase {
 
     public void testProcessBasicLicense() throws Exception {
         RestRequest request = mock(RestRequest.class);
-        when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(false);
+        when(licenseState.isAuthAllowed()).thenReturn(false);
         filter.process(request, channel, null, chain);
         verify(chain).continueProcessing(request, channel, null);
         verifyZeroInteractions(channel, authcService);
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java
index f6ad400e782..2e395d5f16f 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/TransportFilterTests.java
@@ -17,7 +17,9 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.transport.MockTcpTransportPlugin;
 import org.elasticsearch.xpack.security.action.SecurityActionMapper;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.xpack.security.authc.AuthenticationService;
+import org.elasticsearch.xpack.security.authz.AuthorizationService;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESIntegTestCase;
 import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
 import org.elasticsearch.threadpool.ThreadPool;
@@ -288,8 +290,8 @@ public class TransportFilterTests extends ESIntegTestCase {
                                                     AuthenticationService authcService, AuthorizationService authzService,
                                                     SecurityActionMapper actionMapper, ClientTransportFilter clientTransportFilter) {
             super(settings, transport, threadPool, authcService, authzService, actionMapper, clientTransportFilter,
-                    mock(SecurityLicenseState.class));
-            when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(true);
+                    mock(XPackLicenseState.class));
+            when(licenseState.isAuthAllowed()).thenReturn(true);
         }
 
         @Override
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/filter/IPFilterTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/filter/IPFilterTests.java
index fadac74c3eb..17e8fdac070 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/filter/IPFilterTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/filter/IPFilterTests.java
@@ -15,7 +15,7 @@ import org.elasticsearch.common.transport.InetSocketTransportAddress;
 import org.elasticsearch.common.transport.TransportAddress;
 import org.elasticsearch.http.HttpServerTransport;
 import org.elasticsearch.xpack.security.audit.AuditTrail;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.junit.annotations.Network;
 import org.elasticsearch.transport.Transport;
@@ -45,7 +45,7 @@ import static org.mockito.Mockito.when;
  */
 public class IPFilterTests extends ESTestCase {
     private IPFilter ipFilter;
-    private SecurityLicenseState licenseState;
+    private XPackLicenseState licenseState;
     private AuditTrailService auditTrail;
     private Transport transport;
     private HttpServerTransport httpTransport;
@@ -53,8 +53,8 @@ public class IPFilterTests extends ESTestCase {
 
     @Before
     public void init() {
-        licenseState = mock(SecurityLicenseState.class);
-        when(licenseState.ipFilteringEnabled()).thenReturn(true);
+        licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isIpFilteringAllowed()).thenReturn(true);
         auditTrail = mock(AuditTrailService.class);
         clusterSettings = new ClusterSettings(Settings.EMPTY, new HashSet<>(Arrays.asList(
                 IPFilter.HTTP_FILTER_ALLOW_SETTING,
@@ -218,7 +218,7 @@ public class IPFilterTests extends ESTestCase {
         Settings settings = Settings.builder()
                 .put("xpack.security.transport.filter.deny", "_all")
                 .build();
-        when(licenseState.ipFilteringEnabled()).thenReturn(false);
+        when(licenseState.isIpFilteringAllowed()).thenReturn(false);
         ipFilter = new IPFilter(settings, auditTrail, clusterSettings, licenseState);
         ipFilter.setBoundTransportAddress(transport.boundAddress(), transport.profileBoundAddresses());
 
@@ -229,7 +229,7 @@ public class IPFilterTests extends ESTestCase {
         verifyZeroInteractions(auditTrail);
 
         // for sanity enable license and check that it is denied
-        when(licenseState.ipFilteringEnabled()).thenReturn(true);
+        when(licenseState.isIpFilteringAllowed()).thenReturn(true);
         ipFilter = new IPFilter(settings, auditTrail, clusterSettings, licenseState);
         ipFilter.setBoundTransportAddress(transport.boundAddress(), transport.profileBoundAddresses());
 
diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/IPFilterNetty3UpstreamHandlerTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/IPFilterNetty3UpstreamHandlerTests.java
index db81a953475..2c696d1ba4b 100644
--- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/IPFilterNetty3UpstreamHandlerTests.java
+++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/transport/netty3/IPFilterNetty3UpstreamHandlerTests.java
@@ -13,8 +13,7 @@ import org.elasticsearch.common.transport.BoundTransportAddress;
 import org.elasticsearch.common.transport.InetSocketTransportAddress;
 import org.elasticsearch.common.transport.TransportAddress;
 import org.elasticsearch.http.HttpServerTransport;
-import org.elasticsearch.xpack.security.audit.AuditTrail;
-import org.elasticsearch.xpack.security.SecurityLicenseState;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.security.audit.AuditTrailService;
 import org.elasticsearch.xpack.security.transport.filter.IPFilter;
 import org.elasticsearch.test.ESTestCase;
@@ -67,8 +66,8 @@ public class IPFilterNetty3UpstreamHandlerTests extends ESTestCase {
                 IPFilter.TRANSPORT_FILTER_ALLOW_SETTING,
                 IPFilter.TRANSPORT_FILTER_DENY_SETTING,
                 TransportSettings.TRANSPORT_PROFILES_SETTING)));
-        SecurityLicenseState licenseState = mock(SecurityLicenseState.class);
-        when(licenseState.ipFilteringEnabled()).thenReturn(true);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isIpFilteringAllowed()).thenReturn(true);
         AuditTrailService auditTrailService = new AuditTrailService(settings, Collections.emptyList(), licenseState);
         IPFilter ipFilter = new IPFilter(settings, auditTrailService, clusterSettings, licenseState);
         ipFilter.setBoundTransportAddress(transport.boundAddress(), transport.profileBoundAddresses());
diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/license/plugin/core/XPackLicenseState.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/license/plugin/core/XPackLicenseState.java
new file mode 100644
index 00000000000..7e4c7a77eca
--- /dev/null
+++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/license/plugin/core/XPackLicenseState.java
@@ -0,0 +1,349 @@
+/*
+ * 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 java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.logging.LoggerMessageFormat;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.core.License.OperationMode;
+import org.elasticsearch.xpack.graph.Graph;
+import org.elasticsearch.xpack.monitoring.Monitoring;
+import org.elasticsearch.xpack.monitoring.MonitoringSettings;
+import org.elasticsearch.xpack.security.Security;
+import org.elasticsearch.xpack.watcher.Watcher;
+
+/**
+ * A holder for the current state of the license for all xpack features.
+ */
+public class XPackLicenseState {
+
+    /** Messages for each feature which are printed when the license expires. */
+    static final Map<String, String[]> EXPIRATION_MESSAGES;
+    static {
+        Map<String, String[]> messages = new LinkedHashMap<>();
+        messages.put(Security.NAME, new String[] {
+            "Cluster health, cluster stats and indices stats operations are blocked",
+            "All data operations (read and write) continue to work"
+        });
+        messages.put(Watcher.NAME, new String[] {
+            "PUT / GET watch APIs are disabled, DELETE watch API continues to work",
+            "Watches execute and write to the history",
+            "The actions of the watches don't execute"
+        });
+        messages.put(Monitoring.NAME, new String[] {
+            "The agent will stop collecting cluster and indices metrics",
+            "The agent will stop automatically cleaning indices older than [xpack.monitoring.history.duration]"
+        });
+        messages.put(Graph.NAME, new String[] {
+            "Graph explore APIs are disabled"
+        });
+        EXPIRATION_MESSAGES = Collections.unmodifiableMap(messages);
+    }
+
+    /**
+     * Messages for each feature which are printed when the license type changes.
+     * The value is a function taking the old and new license type, and returns the messages for that feature.
+     */
+    static final Map<String, BiFunction<OperationMode, OperationMode, String[]>> ACKNOWLEDGMENT_MESSAGES;
+    static {
+        Map<String, BiFunction<OperationMode, OperationMode, String[]>> messages = new LinkedHashMap<>();
+        messages.put(Security.NAME, XPackLicenseState::securityAcknowledgementMessages);
+        messages.put(Watcher.NAME, XPackLicenseState::watcherAcknowledgementMessages);
+        messages.put(Monitoring.NAME, XPackLicenseState::monitoringAcknowledgementMessages);
+        messages.put(Graph.NAME, XPackLicenseState::graphAcknowledgementMessages);
+        ACKNOWLEDGMENT_MESSAGES = Collections.unmodifiableMap(messages);
+    }
+
+    private static String[] securityAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
+        switch (newMode) {
+            case BASIC:
+                switch (currentMode) {
+                    case TRIAL:
+                    case STANDARD:
+                    case GOLD:
+                    case PLATINUM:
+                        return new String[] {
+                            "The following X-Pack security functionality will be disabled: authentication, authorization, " +
+                                "ip filtering, and auditing. Please restart your node after applying the license.",
+                            "Field and document level access control will be disabled.",
+                            "Custom realms will be ignored."
+                        };
+                }
+                break;
+            case GOLD:
+                switch (currentMode) {
+                    case BASIC:
+                    case STANDARD:
+                        // ^^ though technically it was already disabled, it's not bad to remind them
+                    case TRIAL:
+                    case PLATINUM:
+                        return new String[] {
+                            "Field and document level access control will be disabled.",
+                            "Custom realms will be ignored."
+                        };
+                }
+                break;
+            case STANDARD:
+                switch (currentMode) {
+                    case BASIC:
+                        // ^^ though technically it was already disabled, it's not bad to remind them
+                    case GOLD:
+                    case PLATINUM:
+                    case TRIAL:
+                        return new String[] {
+                            "Authentication will be limited to the native realms.",
+                            "IP filtering and auditing will be disabled.",
+                            "Field and document level access control will be disabled.",
+                            "Custom realms will be ignored."
+                        };
+                }
+        }
+        return Strings.EMPTY_ARRAY;
+    }
+
+    private static String[] watcherAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
+        switch (newMode) {
+            case BASIC:
+                switch (currentMode) {
+                    case TRIAL:
+                    case STANDARD:
+                    case GOLD:
+                    case PLATINUM:
+                        return new String[] { "Watcher will be disabled" };
+                }
+                break;
+        }
+        return Strings.EMPTY_ARRAY;
+    }
+
+    private static String[] monitoringAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
+        switch (newMode) {
+            case BASIC:
+                switch (currentMode) {
+                    case TRIAL:
+                    case STANDARD:
+                    case GOLD:
+                    case PLATINUM:
+                        return new String[] {
+                            LoggerMessageFormat.format(
+                                "Multi-cluster support is disabled for clusters with [{}] license. If you are\n" +
+                                    "running multiple clusters, users won't be able to access the clusters with\n" +
+                                    "[{}] licenses from within a single X-Pack Kibana instance. You will have to deploy a\n" +
+                                    "separate and dedicated X-pack Kibana instance for each [{}] cluster you wish to monitor.",
+                                newMode, newMode, newMode),
+                            LoggerMessageFormat.format(
+                                "Automatic index cleanup is locked to {} days for clusters with [{}] license.",
+                                MonitoringSettings.HISTORY_DURATION.getDefault(Settings.EMPTY).days(), newMode)
+                        };
+                }
+                break;
+        }
+        return Strings.EMPTY_ARRAY;
+    }
+
+    private static String[] graphAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
+        switch (newMode) {
+            case BASIC:
+            case STANDARD:
+            case GOLD:
+                switch (currentMode) {
+                    case TRIAL:
+                    case PLATINUM:
+                        return new String[] { "Graph will be disabled" };
+                }
+                break;
+        }
+        return Strings.EMPTY_ARRAY;
+    }
+
+    /** A wrapper for the license mode and state, to allow atomically swapping. */
+    private static class Status {
+
+        /** The current "mode" of the license (ie license type). */
+        final OperationMode mode;
+
+        /** True if the license is active, or false if it is expired. */
+        final boolean active;
+
+        Status(OperationMode mode, boolean active) {
+            this.mode = mode;
+            this.active = active;
+        }
+    }
+    private volatile Status status = new Status(OperationMode.TRIAL, true);
+
+    /** Updates the current state of the license, which will change what features are available. */
+    void update(OperationMode mode, boolean active) {
+        status = new Status(mode, active);
+    }
+
+    /** Return the current license type. */
+    public OperationMode getOperationMode() {
+        return status.mode;
+    }
+
+    /** Return true if the license is currently within its time boundaries, false otherwise. */
+    public boolean isActive() {
+        return status.active;
+    }
+
+    /**
+     * @return true if authentication and authorization should be enabled. this does not indicate what realms are available
+     * @see #allowedRealmType() for the enabled realms
+     */
+    public boolean isAuthAllowed() {
+        OperationMode mode = status.mode;
+        return mode == OperationMode.STANDARD || mode == OperationMode.GOLD || mode == OperationMode.PLATINUM
+            || mode == OperationMode.TRIAL;
+    }
+
+    /**
+     * @return true if IP filtering should be enabled
+     */
+    public boolean isIpFilteringAllowed() {
+        OperationMode mode = status.mode;
+        return mode == OperationMode.GOLD || mode == OperationMode.PLATINUM || mode == OperationMode.TRIAL;
+    }
+
+    /**
+     * @return true if auditing should be enabled
+     */
+    public boolean isAuditingAllowed() {
+        OperationMode mode = status.mode;
+        return mode == OperationMode.GOLD || mode == OperationMode.PLATINUM || mode == OperationMode.TRIAL;
+    }
+
+    /**
+     * Indicates whether the stats and health API calls should be allowed. If a license is expired and past the grace
+     * period then we deny these calls.
+     *
+     * @return true if the license allows for the stats and health APIs to be used.
+     */
+    public boolean isStatsAndHealthAllowed() {
+        return status.active;
+    }
+
+    /**
+     * Determine if Document Level Security (DLS) and Field Level Security (FLS) should be enabled.
+     * <p>
+     * DLS and FLS are only disabled when the mode is not:
+     * <ul>
+     * <li>{@link OperationMode#PLATINUM}</li>
+     * <li>{@link OperationMode#TRIAL}</li>
+     * </ul>
+     * Note: This does not consider the <em>state</em> of the license so that Security does not suddenly leak information!
+     *
+     * @return {@code true} to enable DLS and FLS. Otherwise {@code false}.
+     */
+    public boolean isDocumentAndFieldLevelSecurityAllowed() {
+        OperationMode mode = status.mode;
+        return mode == OperationMode.TRIAL || mode == OperationMode.PLATINUM;
+    }
+
+    /** Classes of realms that may be available based on the license type. */
+    public enum AllowedRealmType {
+        NONE,
+        NATIVE,
+        DEFAULT,
+        ALL
+    }
+
+    /**
+     * @return the type of realms that are enabled based on the license {@link OperationMode}
+     */
+    public AllowedRealmType allowedRealmType() {
+        switch (status.mode) {
+            case PLATINUM:
+            case TRIAL:
+                return AllowedRealmType.ALL;
+            case GOLD:
+                return AllowedRealmType.DEFAULT;
+            case STANDARD:
+                return AllowedRealmType.NATIVE;
+            default:
+                return AllowedRealmType.NONE;
+        }
+    }
+
+    /**
+     * Determine if Watcher is available based on the current license.
+     * <p>
+     * Watcher is available if the license is active (hasn't expired) and of one of the following types:
+     * <ul>
+     * <li>{@link OperationMode#PLATINUM}</li>
+     * <li>{@link OperationMode#GOLD}</li>
+     * <li>{@link OperationMode#TRIAL}</li>
+     * </ul>
+     *
+     * @return {@code true} as long as the license is valid. Otherwise {@code false}.
+     */
+    public boolean isWatcherAllowed() {
+        // status is volatile, so a local variable is used for a consistent view
+        Status localStatus = status;
+
+        if (localStatus.active == false) {
+            return false;
+        }
+
+        switch (localStatus.mode) {
+            case TRIAL:
+            case GOLD:
+            case PLATINUM:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Monitoring is always available as long as there is a valid license
+     *
+     * @return true
+     */
+    public boolean isMonitoringAllowed() {
+        return status.active;
+    }
+
+    /**
+     * Determine if the current license allows the retention of indices to be modified.
+     * <p>
+     * Only users with a non-{@link OperationMode#BASIC} license can update the retention period.
+     * <p>
+     * Note: This does not consider the <em>state</em> of the license so that any change is remembered for when they fix their license.
+     *
+     * @return {@code true} if the user is allowed to modify the retention. Otherwise {@code false}.
+     */
+    public boolean isUpdateRetentionAllowed() {
+        final OperationMode mode = status.mode;
+        return mode != OperationMode.BASIC && mode != OperationMode.MISSING;
+    }
+
+    /**
+     * Determine if Graph Exploration should be enabled.
+     * <p>
+     * Exploration is only disabled when the license has expired or if the mode is not:
+     * <ul>
+     * <li>{@link OperationMode#PLATINUM}</li>
+     * <li>{@link OperationMode#TRIAL}</li>
+     * </ul>
+     *
+     * @return {@code true} as long as the license is valid. Otherwise {@code false}.
+     */
+    public boolean isGraphAllowed() {
+        // status is volatile
+        Status localStatus = status;
+        OperationMode operationMode = localStatus.mode;
+
+        boolean licensed = operationMode == OperationMode.TRIAL || operationMode == OperationMode.PLATINUM;
+
+        return licensed && localStatus.active;
+    }
+}
diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java
index 6543a8332bb..1208b6e20f4 100644
--- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java
+++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java
@@ -30,6 +30,7 @@ import org.elasticsearch.common.component.LifecycleComponent;
 import org.elasticsearch.common.inject.Binder;
 import org.elasticsearch.common.inject.Module;
 import org.elasticsearch.common.inject.multibindings.Multibinder;
+import org.elasticsearch.common.inject.util.Providers;
 import org.elasticsearch.common.network.NetworkModule;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
@@ -37,6 +38,7 @@ import org.elasticsearch.env.Environment;
 import org.elasticsearch.index.IndexModule;
 import org.elasticsearch.ingest.Processor;
 import org.elasticsearch.license.plugin.Licensing;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.plugins.ActionPlugin;
 import org.elasticsearch.plugins.IngestPlugin;
 import org.elasticsearch.plugins.Plugin;
@@ -121,6 +123,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
     protected boolean transportClientMode;
     protected final XPackExtensionsService extensionsService;
 
+    protected XPackLicenseState licenseState;
     protected Licensing licensing;
     protected Security security;
     protected Monitoring monitoring;
@@ -132,9 +135,10 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
         this.settings = settings;
         this.transportClientMode = transportClientMode(settings);
         this.env = transportClientMode ? null : new Environment(settings);
+        this.licenseState = new XPackLicenseState();
 
         this.licensing = new Licensing(settings);
-        this.security = new Security(settings, env);
+        this.security = new Security(settings, env, licenseState);
         this.monitoring = new Monitoring(settings);
         this.watcher = new Watcher(settings);
         this.graph = new Graph(settings);
@@ -171,6 +175,8 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
             modules.add(new TextTemplateModule());
             // Note: this only exists so LicenseService subclasses can be bound in mock tests
             modules.addAll(licensing.nodeModules());
+        } else {
+            modules.add(b -> b.bind(XPackLicenseState.class).toProvider(Providers.of(null)));
         }
         return modules;
     }
@@ -190,8 +196,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
         final InternalClient internalClient = new InternalClient(settings, threadPool, client, security.getCryptoService());
         components.add(internalClient);
 
-        components.addAll(licensing.createComponents(clusterService, getClock(), env, resourceWatcherService,
-                                                     security.getSecurityLicenseState()));
+        components.addAll(licensing.createComponents(clusterService, getClock(), env, resourceWatcherService, licenseState));
         components.addAll(security.createComponents(internalClient, threadPool, clusterService, resourceWatcherService,
                                                     extensionsService.getExtensions()));
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java
index daa629cfae7..ad5aa66073b 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java
@@ -12,6 +12,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.XPackFeatureSet;
 
 import java.io.IOException;
@@ -24,15 +25,15 @@ import java.util.Map;
 public class WatcherFeatureSet implements XPackFeatureSet {
 
     private final boolean enabled;
-    private final WatcherLicensee licensee;
+    private final XPackLicenseState licenseState;
     private final WatcherService watcherService;
 
     @Inject
-    public WatcherFeatureSet(Settings settings, @Nullable WatcherLicensee licensee, NamedWriteableRegistry namedWriteableRegistry,
+    public WatcherFeatureSet(Settings settings, @Nullable XPackLicenseState licenseState, NamedWriteableRegistry namedWriteableRegistry,
                              @Nullable WatcherService watcherService) {
         this.watcherService = watcherService;
         this.enabled = Watcher.enabled(settings);
-        this.licensee = licensee;
+        this.licenseState = licenseState;
         namedWriteableRegistry.register(Usage.class, Usage.writeableName(Watcher.NAME), Usage::new);
     }
 
@@ -48,7 +49,7 @@ public class WatcherFeatureSet implements XPackFeatureSet {
 
     @Override
     public boolean available() {
-        return licensee != null && licensee.isAvailable();
+        return licenseState != null && licenseState.isWatcherAllowed();
     }
 
     @Override
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java
deleted file mode 100644
index a9264f27f4e..00000000000
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java
+++ /dev/null
@@ -1,94 +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.xpack.watcher;
-
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License.OperationMode;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
-
-public class WatcherLicensee extends AbstractLicenseeComponent {
-
-    public static final String ID = Watcher.NAME;
-
-    public WatcherLicensee(Settings settings) {
-        super(settings, ID);
-    }
-
-    @Override
-    public String[] expirationMessages() {
-        return new String[] {
-                "PUT / GET watch APIs are disabled, DELETE watch API continues to work",
-                "Watches execute and write to the history",
-                "The actions of the watches don't execute"
-        };
-    }
-
-    @Override
-    public String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode) {
-        switch (newMode) {
-            case BASIC:
-                switch (currentMode) {
-                    case TRIAL:
-                    case STANDARD:
-                    case GOLD:
-                    case PLATINUM:
-                        return new String[] { "Watcher will be disabled" };
-                }
-                break;
-        }
-        return Strings.EMPTY_ARRAY;
-    }
-
-    /**
-     * Determine if Watcher is available based on the current license.
-     * <p>
-     * Watcher is available if the license is active (hasn't expired) and of one of the following types:
-     * <ul>
-     * <li>{@link OperationMode#PLATINUM}</li>
-     * <li>{@link OperationMode#GOLD}</li>
-     * <li>{@link OperationMode#TRIAL}</li>
-     * </ul>
-     *
-     * @return {@code true} as long as the license is valid. Otherwise {@code false}.
-     */
-    public boolean isAvailable() {
-        // status is volatile, so a local variable is used for a consistent view
-        Status localStatus = status;
-
-        if (localStatus.isActive() == false) {
-            return false;
-        }
-
-        switch (localStatus.getMode()) {
-            case TRIAL:
-            case GOLD:
-            case PLATINUM:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    public boolean isExecutingActionsAllowed() {
-        return isWatcherTransportActionAllowed();
-    }
-
-    public boolean isGetWatchAllowed() {
-        return isWatcherTransportActionAllowed();
-    }
-
-    public boolean isPutWatchAllowed() {
-        return isWatcherTransportActionAllowed();
-    }
-
-
-    public boolean isWatcherTransportActionAllowed() {
-        return isAvailable();
-    }
-
-
-}
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionRegistry.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionRegistry.java
index 4ef05636691..af126a8fbbb 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionRegistry.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionRegistry.java
@@ -8,7 +8,7 @@ package org.elasticsearch.xpack.watcher.actions;
 import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.support.clock.Clock;
 import org.elasticsearch.xpack.watcher.support.validation.Validation;
 import org.elasticsearch.xpack.watcher.transform.TransformRegistry;
@@ -25,15 +25,15 @@ public class ActionRegistry {
     private final Map<String, ActionFactory> parsers;
     private final TransformRegistry transformRegistry;
     private final Clock clock;
-    private final WatcherLicensee watcherLicensee;
+    private final XPackLicenseState licenseState;
 
     @Inject
     public ActionRegistry(Map<String, ActionFactory> parsers, TransformRegistry transformRegistry, Clock clock,
-                          WatcherLicensee watcherLicensee) {
+                          XPackLicenseState licenseState) {
         this.parsers = parsers;
         this.transformRegistry = transformRegistry;
         this.clock = clock;
-        this.watcherLicensee = watcherLicensee;
+        this.licenseState = licenseState;
     }
 
     ActionFactory factory(String type) {
@@ -57,7 +57,7 @@ public class ActionRegistry {
                     throw new ElasticsearchParseException("could not parse action [{}] for watch [{}]. {}", id, watchId, error);
                 }
             } else if (token == XContentParser.Token.START_OBJECT && id != null) {
-                ActionWrapper action = ActionWrapper.parse(watchId, id, parser, this, transformRegistry, clock, watcherLicensee);
+                ActionWrapper action = ActionWrapper.parse(watchId, id, parser, this, transformRegistry, clock, licenseState);
                 actions.add(action);
             }
         }
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java
index 8e71fab6e60..056ae4779fb 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java
@@ -14,10 +14,10 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.watcher.actions.throttler.ActionThrottler;
 import org.elasticsearch.xpack.watcher.actions.throttler.Throttler;
 import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
 import org.elasticsearch.xpack.support.clock.Clock;
 import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
@@ -140,7 +140,7 @@ public class ActionWrapper implements ToXContent {
 
     static ActionWrapper parse(String watchId, String actionId, XContentParser parser,
                                ActionRegistry actionRegistry, TransformRegistry transformRegistry,
-                               Clock clock, WatcherLicensee watcherLicensee) throws IOException {
+                               Clock clock, XPackLicenseState licenseState) throws IOException {
 
         assert parser.currentToken() == XContentParser.Token.START_OBJECT;
 
@@ -178,7 +178,7 @@ public class ActionWrapper implements ToXContent {
             throw new ElasticsearchParseException("could not parse watch action [{}/{}]. missing action type", watchId, actionId);
         }
 
-        ActionThrottler throttler = new ActionThrottler(clock, throttlePeriod, watcherLicensee);
+        ActionThrottler throttler = new ActionThrottler(clock, throttlePeriod, licenseState);
         return new ActionWrapper(actionId, throttler, transform, action);
     }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/throttler/ActionThrottler.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/throttler/ActionThrottler.java
index 25eb9b00010..d5fb240a574 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/throttler/ActionThrottler.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/actions/throttler/ActionThrottler.java
@@ -7,8 +7,8 @@ package org.elasticsearch.xpack.watcher.actions.throttler;
 
 import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.support.clock.Clock;
 
 /**
@@ -18,18 +18,18 @@ public class ActionThrottler implements Throttler {
 
     private static final AckThrottler ACK_THROTTLER = new AckThrottler();
 
-    private final WatcherLicensee watcherLicensee;
+    private final XPackLicenseState licenseState;
     private final PeriodThrottler periodThrottler;
     private final AckThrottler ackThrottler;
 
-    public ActionThrottler(Clock clock, @Nullable TimeValue throttlePeriod, WatcherLicensee watcherLicensee) {
-        this(new PeriodThrottler(clock, throttlePeriod), ACK_THROTTLER, watcherLicensee);
+    public ActionThrottler(Clock clock, @Nullable TimeValue throttlePeriod, XPackLicenseState licenseState) {
+        this(new PeriodThrottler(clock, throttlePeriod), ACK_THROTTLER, licenseState);
     }
 
-    ActionThrottler(PeriodThrottler periodThrottler, AckThrottler ackThrottler, WatcherLicensee watcherLicensee) {
+    ActionThrottler(PeriodThrottler periodThrottler, AckThrottler ackThrottler, XPackLicenseState licenseState) {
         this.periodThrottler = periodThrottler;
         this.ackThrottler = ackThrottler;
-        this.watcherLicensee = watcherLicensee;
+        this.licenseState = licenseState;
     }
 
     public TimeValue throttlePeriod() {
@@ -38,7 +38,7 @@ public class ActionThrottler implements Throttler {
 
     @Override
     public Result throttle(String actionId, WatchExecutionContext ctx) {
-        if (!watcherLicensee.isExecutingActionsAllowed()) {
+        if (licenseState.isWatcherAllowed() == false) {
             return Result.throttle("watcher license does not allow action execution");
         }
         if (periodThrottler != null) {
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/WatcherTransportAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/WatcherTransportAction.java
index 74dcd226922..a884e2f1068 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/WatcherTransportAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/WatcherTransportAction.java
@@ -14,10 +14,11 @@ import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.license.plugin.core.LicenseUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
+import org.elasticsearch.xpack.watcher.Watcher;
 
 import java.util.function.Supplier;
 
@@ -27,22 +28,22 @@ import java.util.function.Supplier;
 public abstract class WatcherTransportAction<Request extends MasterNodeRequest<Request>, Response extends ActionResponse>
         extends TransportMasterNodeAction<Request, Response> {
 
-    protected final WatcherLicensee watcherLicensee;
+    protected final XPackLicenseState licenseState;
 
     public WatcherTransportAction(Settings settings, String actionName, TransportService transportService,
                                   ClusterService clusterService, ThreadPool threadPool, ActionFilters actionFilters,
-                                  IndexNameExpressionResolver indexNameExpressionResolver, WatcherLicensee watcherLicensee,
+                                  IndexNameExpressionResolver indexNameExpressionResolver, XPackLicenseState licenseState,
                                   Supplier<Request> request) {
         super(settings, actionName, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, request);
-        this.watcherLicensee = watcherLicensee;
+        this.licenseState = licenseState;
     }
 
     @Override
     protected void doExecute(Task task, Request request, ActionListener<Response> listener) {
-        if (watcherLicensee.isWatcherTransportActionAllowed()) {
+        if (licenseState.isWatcherAllowed()) {
             super.doExecute(task, request, listener);
         } else {
-            listener.onFailure(LicenseUtils.newComplianceException(WatcherLicensee.ID));
+            listener.onFailure(LicenseUtils.newComplianceException(Watcher.NAME));
         }
     }
 }
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/ack/TransportAckWatchAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/ack/TransportAckWatchAction.java
index 5706d95c759..0ccf499a414 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/ack/TransportAckWatchAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/ack/TransportAckWatchAction.java
@@ -15,10 +15,10 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.watcher.WatcherService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 import org.elasticsearch.xpack.watcher.watch.WatchStatus;
 import org.elasticsearch.xpack.watcher.watch.WatchStore;
@@ -34,9 +34,9 @@ public class TransportAckWatchAction extends WatcherTransportAction<AckWatchRequ
     public TransportAckWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                    ThreadPool  threadPool, ActionFilters actionFilters,
                                    IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
-                                   WatcherLicensee watcherLicensee) {
+                                   XPackLicenseState licenseState) {
         super(settings, AckWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, AckWatchRequest::new);
+                licenseState, AckWatchRequest::new);
         this.watcherService = watcherService;
     }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/activate/TransportActivateWatchAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/activate/TransportActivateWatchAction.java
index 930451d32da..307c2122108 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/activate/TransportActivateWatchAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/activate/TransportActivateWatchAction.java
@@ -15,10 +15,10 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.watcher.WatcherService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 import org.elasticsearch.xpack.watcher.watch.WatchStatus;
 import org.elasticsearch.xpack.watcher.watch.WatchStore;
@@ -34,9 +34,9 @@ public class TransportActivateWatchAction extends WatcherTransportAction<Activat
     public TransportActivateWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                         ThreadPool threadPool, ActionFilters actionFilters,
                                         IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
-                                        WatcherLicensee watcherLicensee) {
+                                        XPackLicenseState licenseState) {
         super(settings, ActivateWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, ActivateWatchRequest::new);
+                licenseState, ActivateWatchRequest::new);
         this.watcherService = watcherService;
     }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/delete/TransportDeleteWatchAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/delete/TransportDeleteWatchAction.java
index 74178752ec4..f90c20b9aca 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/delete/TransportDeleteWatchAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/delete/TransportDeleteWatchAction.java
@@ -16,10 +16,10 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.watcher.WatcherService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 import org.elasticsearch.xpack.watcher.watch.WatchStore;
 
@@ -34,9 +34,9 @@ public class TransportDeleteWatchAction extends WatcherTransportAction<DeleteWat
     public TransportDeleteWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                       ThreadPool threadPool, ActionFilters actionFilters,
                                       IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
-                                      WatcherLicensee watcherLicensee) {
+                                      XPackLicenseState licenseState) {
         super(settings, DeleteWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, DeleteWatchRequest::new);
+                licenseState, DeleteWatchRequest::new);
         this.watcherService = watcherService;
     }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/execute/TransportExecuteWatchAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/execute/TransportExecuteWatchAction.java
index 705f07b885c..70d4364aa50 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/execute/TransportExecuteWatchAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/execute/TransportExecuteWatchAction.java
@@ -18,6 +18,7 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.watcher.condition.always.AlwaysCondition;
@@ -26,7 +27,6 @@ import org.elasticsearch.xpack.watcher.execution.ExecutionService;
 import org.elasticsearch.xpack.watcher.execution.ManualExecutionContext;
 import org.elasticsearch.xpack.watcher.history.WatchRecord;
 import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.support.clock.Clock;
 import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
@@ -58,10 +58,10 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
     public TransportExecuteWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                        ThreadPool threadPool, ActionFilters actionFilters,
                                        IndexNameExpressionResolver indexNameExpressionResolver, ExecutionService executionService,
-                                       Clock clock, WatcherLicensee watcherLicensee, WatchStore watchStore, TriggerService triggerService,
+                                       Clock clock, XPackLicenseState licenseState, WatchStore watchStore, TriggerService triggerService,
                                        Watch.Parser watchParser) {
         super(settings, ExecuteWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, ExecuteWatchRequest::new);
+                licenseState, ExecuteWatchRequest::new);
         this.executionService = executionService;
         this.watchStore = watchStore;
         this.clock = clock;
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java
index 224f56052f3..28c821f3484 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java
@@ -19,10 +19,11 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.license.plugin.core.LicenseUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
+import org.elasticsearch.xpack.watcher.Watcher;
 import org.elasticsearch.xpack.watcher.WatcherService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 import org.elasticsearch.xpack.watcher.watch.Watch;
@@ -43,9 +44,9 @@ public class TransportGetWatchAction extends WatcherTransportAction<GetWatchRequ
     public TransportGetWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                    ThreadPool threadPool, ActionFilters actionFilters,
                                    IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
-                                   WatcherLicensee watcherLicensee) {
-        super(settings, GetWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, GetWatchRequest::new);
+                                   XPackLicenseState licenseState) {
+        super(settings, GetWatchAction.NAME, transportService, clusterService, threadPool, actionFilters,
+            indexNameExpressionResolver, licenseState, GetWatchRequest::new);
         this.watcherService = watcherService;
     }
 
@@ -62,8 +63,8 @@ public class TransportGetWatchAction extends WatcherTransportAction<GetWatchRequ
     @Override
     protected void masterOperation(GetWatchRequest request, ClusterState state, ActionListener<GetWatchResponse> listener) throws
             ElasticsearchException {
-        if (!watcherLicensee.isGetWatchAllowed()) {
-            listener.onFailure(LicenseUtils.newComplianceException(WatcherLicensee.ID));
+        if (licenseState.isWatcherAllowed() == false) {
+            listener.onFailure(LicenseUtils.newComplianceException(Watcher.NAME));
             return;
         }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/put/TransportPutWatchAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/put/TransportPutWatchAction.java
index f7636f2cb37..0854a19c127 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/put/TransportPutWatchAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/put/TransportPutWatchAction.java
@@ -17,10 +17,11 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.license.plugin.core.LicenseUtils;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
+import org.elasticsearch.xpack.watcher.Watcher;
 import org.elasticsearch.xpack.watcher.WatcherService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 import org.elasticsearch.xpack.watcher.watch.WatchStore;
 
@@ -34,9 +35,9 @@ public class TransportPutWatchAction extends WatcherTransportAction<PutWatchRequ
     public TransportPutWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                    ThreadPool threadPool, ActionFilters actionFilters,
                                    IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
-                                   WatcherLicensee watcherLicensee) {
-        super(settings, PutWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, PutWatchRequest::new);
+                                   XPackLicenseState licenseState) {
+        super(settings, PutWatchAction.NAME, transportService, clusterService, threadPool, actionFilters,
+            indexNameExpressionResolver, licenseState, PutWatchRequest::new);
         this.watcherService = watcherService;
     }
 
@@ -53,8 +54,8 @@ public class TransportPutWatchAction extends WatcherTransportAction<PutWatchRequ
     @Override
     protected void masterOperation(PutWatchRequest request, ClusterState state, ActionListener<PutWatchResponse> listener) throws
             ElasticsearchException {
-        if (!watcherLicensee.isPutWatchAllowed()) {
-            listener.onFailure(LicenseUtils.newComplianceException(WatcherLicensee.ID));
+        if (licenseState.isWatcherAllowed() == false) {
+            listener.onFailure(LicenseUtils.newComplianceException(Watcher.NAME));
             return;
         }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/service/TransportWatcherServiceAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/service/TransportWatcherServiceAction.java
index b82e76e318a..0ef66da86ea 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/service/TransportWatcherServiceAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/service/TransportWatcherServiceAction.java
@@ -15,10 +15,10 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.watcher.WatcherLifeCycleService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 
 /**
@@ -31,9 +31,9 @@ public class TransportWatcherServiceAction extends WatcherTransportAction<Watche
     public TransportWatcherServiceAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                          ThreadPool threadPool, ActionFilters actionFilters,
                                          IndexNameExpressionResolver indexNameExpressionResolver,
-                                         WatcherLifeCycleService lifeCycleService, WatcherLicensee watcherLicensee) {
+                                         WatcherLifeCycleService lifeCycleService, XPackLicenseState licenseState) {
         super(settings, WatcherServiceAction.NAME, transportService, clusterService, threadPool, actionFilters,
-                indexNameExpressionResolver, watcherLicensee, WatcherServiceRequest::new);
+                indexNameExpressionResolver, licenseState, WatcherServiceRequest::new);
         this.lifeCycleService = lifeCycleService;
     }
 
diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java
index a3943413afd..c606855b164 100644
--- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java
+++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java
@@ -15,13 +15,13 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
 import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.watcher.WatcherBuild;
 import org.elasticsearch.xpack.watcher.WatcherLifeCycleService;
 import org.elasticsearch.xpack.watcher.WatcherService;
 import org.elasticsearch.xpack.watcher.execution.ExecutionService;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
 
 /**
@@ -37,10 +37,10 @@ public class TransportWatcherStatsAction extends WatcherTransportAction<WatcherS
     public TransportWatcherStatsAction(Settings settings, TransportService transportService, ClusterService clusterService,
                                        ThreadPool threadPool, ActionFilters actionFilters,
                                        IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
-                                       ExecutionService executionService, WatcherLicensee watcherLicensee,
+                                       ExecutionService executionService, XPackLicenseState licenseState,
                                        WatcherLifeCycleService lifeCycleService) {
         super(settings, WatcherStatsAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
-                watcherLicensee, WatcherStatsRequest::new);
+                licenseState, WatcherStatsRequest::new);
         this.watcherService = watcherService;
         this.executionService = executionService;
         this.lifeCycleService = lifeCycleService;
diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java
index 1736ee5997d..f12743353cb 100644
--- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java
+++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherFeatureSetTests.java
@@ -9,6 +9,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
 import org.junit.Before;
@@ -31,26 +32,26 @@ import static org.mockito.Mockito.when;
  */
 public class WatcherFeatureSetTests extends ESTestCase {
 
-    private WatcherLicensee licensee;
+    private XPackLicenseState licenseState;
     private NamedWriteableRegistry namedWriteableRegistry;
     private WatcherService watcherService;
 
     @Before
     public void init() throws Exception {
-        licensee = mock(WatcherLicensee.class);
+        licenseState = mock(XPackLicenseState.class);
         namedWriteableRegistry = mock(NamedWriteableRegistry.class);
         watcherService = mock(WatcherService.class);
     }
 
     public void testWritableRegistration() throws Exception {
-        new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry, watcherService);
+        new WatcherFeatureSet(Settings.EMPTY, licenseState, namedWriteableRegistry, watcherService);
         verify(namedWriteableRegistry).register(eq(WatcherFeatureSet.Usage.class), eq("xpack.usage.watcher"), anyObject());
     }
 
     public void testAvailable() throws Exception {
-        WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry, watcherService);
+        WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licenseState, namedWriteableRegistry, watcherService);
         boolean available = randomBoolean();
-        when(licensee.isAvailable()).thenReturn(available);
+        when(licenseState.isWatcherAllowed()).thenReturn(available);
         assertThat(featureSet.available(), is(available));
     }
 
@@ -64,7 +65,7 @@ public class WatcherFeatureSetTests extends ESTestCase {
         } else {
             settings.put("xpack.watcher.enabled", enabled);
         }
-        WatcherFeatureSet featureSet = new WatcherFeatureSet(settings.build(), licensee, namedWriteableRegistry, watcherService);
+        WatcherFeatureSet featureSet = new WatcherFeatureSet(settings.build(), licenseState, namedWriteableRegistry, watcherService);
         assertThat(featureSet.enabled(), is(enabled));
     }
 
@@ -73,7 +74,7 @@ public class WatcherFeatureSetTests extends ESTestCase {
         statsMap.put("foo", "bar");
         when(watcherService.usageStats()).thenReturn(statsMap);
 
-        WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licensee, namedWriteableRegistry, watcherService);
+        WatcherFeatureSet featureSet = new WatcherFeatureSet(Settings.EMPTY, licenseState, namedWriteableRegistry, watcherService);
         XContentBuilder builder = jsonBuilder();
         featureSet.usage().toXContent(builder, ToXContent.EMPTY_PARAMS);
 
diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/throttler/WatchThrottlerTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/throttler/WatchThrottlerTests.java
index ca4a94a79cb..a63e9cc217b 100644
--- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/throttler/WatchThrottlerTests.java
+++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/throttler/WatchThrottlerTests.java
@@ -5,9 +5,9 @@
  */
 package org.elasticsearch.xpack.watcher.actions.throttler;
 
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
@@ -26,9 +26,9 @@ public class WatchThrottlerTests extends ESTestCase {
         when(periodThrottler.throttle("_action", ctx)).thenReturn(Throttler.Result.NO);
         Throttler.Result expectedResult = Throttler.Result.throttle("_reason");
         when(ackThrottler.throttle("_action", ctx)).thenReturn(expectedResult);
-        WatcherLicensee watcherLicensee = mock(WatcherLicensee.class);
-        when(watcherLicensee.isExecutingActionsAllowed()).thenReturn(true);
-        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, watcherLicensee);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isWatcherAllowed()).thenReturn(true);
+        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, licenseState);
         Throttler.Result result = throttler.throttle("_action", ctx);
         assertThat(result, notNullValue());
         assertThat(result, is(expectedResult));
@@ -41,9 +41,9 @@ public class WatchThrottlerTests extends ESTestCase {
         Throttler.Result expectedResult = Throttler.Result.throttle("_reason");
         when(periodThrottler.throttle("_action", ctx)).thenReturn(expectedResult);
         when(ackThrottler.throttle("_action", ctx)).thenReturn(Throttler.Result.NO);
-        WatcherLicensee watcherLicensee = mock(WatcherLicensee.class);
-        when(watcherLicensee.isExecutingActionsAllowed()).thenReturn(true);
-        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, watcherLicensee);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isWatcherAllowed()).thenReturn(true);
+        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, licenseState);
         Throttler.Result result = throttler.throttle("_action", ctx);
         assertThat(result, notNullValue());
         assertThat(result, is(expectedResult));
@@ -57,9 +57,9 @@ public class WatchThrottlerTests extends ESTestCase {
         when(periodThrottler.throttle("_action", ctx)).thenReturn(periodResult);
         Throttler.Result ackResult = Throttler.Result.throttle("_reason_ack");
         when(ackThrottler.throttle("_action", ctx)).thenReturn(ackResult);
-        WatcherLicensee watcherLicensee = mock(WatcherLicensee.class);
-        when(watcherLicensee.isExecutingActionsAllowed()).thenReturn(true);
-        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, watcherLicensee);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isWatcherAllowed()).thenReturn(true);
+        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, licenseState);
         Throttler.Result result = throttler.throttle("_action", ctx);
         assertThat(result, notNullValue());
         // we always check the period first... so the result will come for the period throttler
@@ -72,9 +72,9 @@ public class WatchThrottlerTests extends ESTestCase {
         WatchExecutionContext ctx = mock(WatchExecutionContext.class);
         when(periodThrottler.throttle("_action", ctx)).thenReturn(Throttler.Result.NO);
         when(ackThrottler.throttle("_action", ctx)).thenReturn(Throttler.Result.NO);
-        WatcherLicensee watcherLicensee = mock(WatcherLicensee.class);
-        when(watcherLicensee.isExecutingActionsAllowed()).thenReturn(true);
-        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, watcherLicensee);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isWatcherAllowed()).thenReturn(true);
+        ActionThrottler throttler = new ActionThrottler(periodThrottler, ackThrottler, licenseState);
         Throttler.Result result = throttler.throttle("_action", ctx);
         assertThat(result, notNullValue());
         assertThat(result, is(Throttler.Result.NO));
@@ -85,9 +85,9 @@ public class WatchThrottlerTests extends ESTestCase {
         WatchExecutionContext ctx = mock(WatchExecutionContext.class);
         Throttler.Result ackResult = mock(Throttler.Result.class);
         when(ackThrottler.throttle("_action", ctx)).thenReturn(ackResult);
-        WatcherLicensee watcherLicensee = mock(WatcherLicensee.class);
-        when(watcherLicensee.isExecutingActionsAllowed()).thenReturn(true);
-        ActionThrottler throttler = new ActionThrottler(null, ackThrottler, watcherLicensee);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isWatcherAllowed()).thenReturn(true);
+        ActionThrottler throttler = new ActionThrottler(null, ackThrottler, licenseState);
         Throttler.Result result = throttler.throttle("_action", ctx);
         assertThat(result, notNullValue());
         assertThat(result, sameInstance(ackResult));
@@ -98,9 +98,9 @@ public class WatchThrottlerTests extends ESTestCase {
         WatchExecutionContext ctx = mock(WatchExecutionContext.class);
         Throttler.Result ackResult = mock(Throttler.Result.class);
         when(ackThrottler.throttle("_action", ctx)).thenReturn(ackResult);
-        WatcherLicensee watcherLicensee = mock(WatcherLicensee.class);
-        when(watcherLicensee.isExecutingActionsAllowed()).thenReturn(false);
-        ActionThrottler throttler = new ActionThrottler(null, ackThrottler, watcherLicensee);
+        XPackLicenseState licenseState = mock(XPackLicenseState.class);
+        when(licenseState.isWatcherAllowed()).thenReturn(false);
+        ActionThrottler throttler = new ActionThrottler(null, ackThrottler, licenseState);
         Throttler.Result result = throttler.throttle("_action", ctx);
         assertThat(result, notNullValue());
         assertThat(result.reason(), is("watcher license does not allow action execution"));
diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/license/LicenseTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/license/LicenseTests.java
deleted file mode 100644
index b994e6c2860..00000000000
--- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/license/LicenseTests.java
+++ /dev/null
@@ -1,92 +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.xpack.watcher.license;
-
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.license.core.License;
-import org.elasticsearch.license.plugin.core.AbstractLicenseeTestCase;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
-
-import static org.elasticsearch.license.core.License.OperationMode.BASIC;
-import static org.elasticsearch.license.core.License.OperationMode.GOLD;
-import static org.elasticsearch.license.core.License.OperationMode.PLATINUM;
-import static org.elasticsearch.license.core.License.OperationMode.STANDARD;
-import static org.elasticsearch.license.core.License.OperationMode.TRIAL;
-import static org.hamcrest.Matchers.is;
-
-public class LicenseTests extends AbstractLicenseeTestCase {
-
-    private WatcherLicensee watcherLicensee;
-
-    public void testPlatinumGoldTrialLicenseCanDoEverything() throws Exception {
-        initLicense(TRIAL, GOLD, PLATINUM);
-        assertWatcherActionsAllowed(watcherLicensee);
-    }
-
-    public void testBasicStandardLicenseDisablesWatcher() throws Exception {
-        initLicense(BASIC, STANDARD);
-        assertWatcherActionsNotAllowed(watcherLicensee);
-    }
-
-    public void testNoLicenseDisablesWatcher() {
-        initLicense(BASIC, STANDARD);
-        disable(watcherLicensee);
-
-        assertWatcherActionsNotAllowed(watcherLicensee);
-    }
-
-    public void testExpiredPlatinumGoldTrialLicenseDisablesWatcher() throws Exception {
-        initLicense(TRIAL, GOLD, PLATINUM);
-        disable(watcherLicensee);
-
-        assertWatcherActionsNotAllowed(watcherLicensee);
-    }
-
-    public void testUpgradingFromBasicOrStandardLicenseWorks() {
-        initLicense(BASIC, STANDARD);
-        assertWatcherActionsNotAllowed(watcherLicensee);
-
-        setOperationMode(watcherLicensee, randomFrom(TRIAL, GOLD, PLATINUM));
-        assertWatcherActionsAllowed(watcherLicensee);
-    }
-
-    public void testDowngradingToBasicOrStandardLicenseWorks() {
-        initLicense(TRIAL, GOLD, PLATINUM);
-        assertWatcherActionsAllowed(watcherLicensee);
-
-        setOperationMode(watcherLicensee, randomFrom(BASIC, STANDARD));
-        assertWatcherActionsNotAllowed(watcherLicensee);
-    }
-
-    public void testUpgradingExpiredLicenseWorks() {
-        initLicense(TRIAL, GOLD, PLATINUM);
-        disable(watcherLicensee);
-
-        assertWatcherActionsNotAllowed(watcherLicensee);
-
-        setOperationMode(watcherLicensee, randomFrom(TRIAL, GOLD, PLATINUM));
-        assertWatcherActionsAllowed(watcherLicensee);
-    }
-
-    private void assertWatcherActionsAllowed(WatcherLicensee watcherLicensee) {
-        assertThat("Expected putting a watch to be allowed", watcherLicensee.isPutWatchAllowed(), is(true));
-        assertThat("Expected getting a watch to be allowed", watcherLicensee.isGetWatchAllowed(), is(true));
-        assertThat("Expected watcher transport actions to be allowed", watcherLicensee.isWatcherTransportActionAllowed(), is(true));
-        assertThat("Expected actions of a watch to be executed", watcherLicensee.isExecutingActionsAllowed(), is(true));
-    }
-
-    private void assertWatcherActionsNotAllowed(WatcherLicensee watcherLicensee) {
-        assertThat("Expected putting a watch not to be allowed", watcherLicensee.isPutWatchAllowed(), is(false));
-        assertThat("Expected getting a watch not to be allowed", watcherLicensee.isGetWatchAllowed(), is(false));
-        assertThat("Expected watcher transport actions not to be allowed", watcherLicensee.isWatcherTransportActionAllowed(), is(false));
-        assertThat("Expected actions of a watch not to be executed", watcherLicensee.isExecutingActionsAllowed(), is(false));
-    }
-
-    private void initLicense(License.OperationMode ... allowedLicenses) {
-        watcherLicensee = new WatcherLicensee(Settings.EMPTY);
-        setOperationMode(watcherLicensee, randomFrom(allowedLicenses));
-    }
-}
diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java
index d79bccbb720..fb9dc6d1b00 100644
--- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java
+++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java
@@ -22,7 +22,7 @@ import org.elasticsearch.common.util.Callback;
 import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.common.xcontent.support.XContentMapValues;
 import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.script.ScriptService;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.xpack.monitoring.Monitoring;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.script.MockMustacheScriptEngine;
@@ -48,7 +48,6 @@ import org.elasticsearch.xpack.watcher.client.WatcherClient;
 import org.elasticsearch.xpack.watcher.execution.ExecutionService;
 import org.elasticsearch.xpack.watcher.execution.ExecutionState;
 import org.elasticsearch.xpack.watcher.history.HistoryStore;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
 import org.elasticsearch.xpack.support.clock.ClockMock;
 import org.elasticsearch.xpack.common.http.HttpClient;
@@ -357,10 +356,6 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
         return randomBoolean() ? new XPackClient(client).watcher() : new WatcherClient(client);
     }
 
-    protected ScriptService scriptService() {
-        return internalCluster().getInstance(ScriptService.class);
-    }
-
     protected HttpClient watcherHttpClient() {
         return internalCluster().getInstance(HttpClient.class);
     }
@@ -369,10 +364,6 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
         return new NoopEmailService();
     }
 
-    protected WatcherLicensee licenseService() {
-        return getInstanceFromMaster(WatcherLicensee.class);
-    }
-
     protected IndexNameExpressionResolver indexNameExpressionResolver() {
         return internalCluster().getInstance(IndexNameExpressionResolver.class);
     }
@@ -541,8 +532,8 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
 
     protected void ensureLicenseEnabled() throws Exception {
         assertBusy(() -> {
-            for (WatcherLicensee service : internalCluster().getInstances(WatcherLicensee.class)) {
-                assertThat(service.isWatcherTransportActionAllowed(), is(true));
+            for (XPackLicenseState licenseState : internalCluster().getInstances(XPackLicenseState.class)) {
+                assertThat(licenseState.isWatcherAllowed(), is(true));
             }
         });
     }
diff --git a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java
index e3bea2db3b3..121bcf639a7 100644
--- a/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java
+++ b/elasticsearch/x-pack/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java
@@ -5,6 +5,14 @@
  */
 package org.elasticsearch.xpack.watcher.watch;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.elasticsearch.ElasticsearchParseException;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.logging.ESLogger;
@@ -16,6 +24,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.index.query.MatchAllQueryBuilder;
 import org.elasticsearch.index.query.QueryParser;
 import org.elasticsearch.indices.query.IndicesQueriesRegistry;
+import org.elasticsearch.license.plugin.core.XPackLicenseState;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.common.http.HttpClient;
@@ -35,7 +44,6 @@ import org.elasticsearch.xpack.notification.email.attachment.EmailAttachmentsPar
 import org.elasticsearch.xpack.support.clock.Clock;
 import org.elasticsearch.xpack.support.clock.ClockMock;
 import org.elasticsearch.xpack.support.clock.SystemClock;
-import org.elasticsearch.xpack.watcher.WatcherLicensee;
 import org.elasticsearch.xpack.watcher.actions.ActionFactory;
 import org.elasticsearch.xpack.watcher.actions.ActionRegistry;
 import org.elasticsearch.xpack.watcher.actions.ActionStatus;
@@ -117,14 +125,6 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.junit.Before;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import static java.util.Collections.singleton;
 import static java.util.Collections.singletonMap;
 import static java.util.Collections.unmodifiableMap;
@@ -148,7 +148,7 @@ public class WatchTests extends ESTestCase {
     private TextTemplateEngine templateEngine;
     private HtmlSanitizer htmlSanitizer;
     private HttpAuthRegistry authRegistry;
-    private WatcherLicensee watcherLicensee;
+    private XPackLicenseState licenseState;
     private ESLogger logger;
     private Settings settings = Settings.EMPTY;
     private WatcherSearchTemplateService searchTemplateService;
@@ -161,7 +161,7 @@ public class WatchTests extends ESTestCase {
         emailService = mock(EmailService.class);
         templateEngine = mock(TextTemplateEngine.class);
         htmlSanitizer = mock(HtmlSanitizer.class);
-        watcherLicensee = mock(WatcherLicensee.class);
+        licenseState = mock(XPackLicenseState.class);
         authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(null)));
         logger = Loggers.getLogger(WatchTests.class);
         searchTemplateService = mock(WatcherSearchTemplateService.class);
@@ -476,12 +476,12 @@ public class WatchTests extends ESTestCase {
                     break;
             }
         }
-        return new ActionRegistry(unmodifiableMap(parsers), transformRegistry, SystemClock.INSTANCE, watcherLicensee);
+        return new ActionRegistry(unmodifiableMap(parsers), transformRegistry, SystemClock.INSTANCE, licenseState);
     }
 
     private ActionThrottler randomThrottler() {
         return new ActionThrottler(SystemClock.INSTANCE, randomBoolean() ? null : TimeValue.timeValueMinutes(randomIntBetween(3, 5)),
-                watcherLicensee);
+                licenseState);
     }
 
     static class ParseOnlyScheduleTriggerEngine extends ScheduleTriggerEngine {