Forráskód Böngészése

[Logstash] Move `elastic_integration` plugin usage to ES logstash-bridge. (#131486)

* Make metadata version field accessible, introduce inverse wrapper with default method to avoid required interface implementation.

* RedactPlugin, IngestCommonPlugin and IngestUserAgent plugins moved to the bridge. Processor definitions also moved to the bridge. Module simplifications.

* Ingest common plugin simplification.

* [CI] Auto commit changes from spotless

* Open an access for the x-pack spatial module resources.

* [CI] Auto commit changes from spotless

* Open access to resources in mapper constant keyword.

* Add an access to resource in x-pack wildcard module.

* Export and open access to sub-package spatial modules.

* Provide module service implementations with provides keyword in x-pack spatial/wildcard/mapper-constant modules.

* Export x-pack spatial module packages to make accessible to ES server module.

* spike refactor of logstash-bridge stable API

 - transitions terminology from wrap/unwrap to toInternal/fromInternal
 - adds abstract base class for ProcessorBridge, since we are expecting external
   implementations, which includes an internal-shaped proxy to the external
   definition.
 - adds copious commentary for the classes that were previously shipped

* Format the recent logstash-bridge changes.

* Wildcard and mapper constant keyword modules open resource access to painless spi

* Rename constant-keyword, wildcard, redact and spatial modules in a way that remove xpack from namings. Move constants from processor bridge to align with a proper place where Ingest common plugin bridge is suitable.

---------

Co-authored-by: elasticsearchmachine <infra-root+elasticsearchmachine@elastic.co>
Co-authored-by: Ry Biesemeyer <ry.biesemeyer@elastic.co>
Mashhur 3 hónapja
szülő
commit
1e87f6753d
24 módosított fájl, 548 hozzáadás és 138 törlés
  1. 7 1
      libs/logstash-bridge/build.gradle
  2. 7 0
      libs/logstash-bridge/src/main/java/module-info.java
  3. 24 18
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/StableBridgeAPI.java
  4. 15 9
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/common/SettingsBridge.java
  5. 3 0
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/core/IOUtilsBridge.java
  6. 8 5
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/env/EnvironmentBridge.java
  7. 4 1
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/ConfigurationUtilsBridge.java
  8. 26 16
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/IngestDocumentBridge.java
  9. 12 9
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/PipelineBridge.java
  10. 10 7
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/PipelineConfigurationBridge.java
  11. 108 36
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/ProcessorBridge.java
  12. 65 0
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/IngestCommonPluginBridge.java
  13. 17 8
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/IngestPluginBridge.java
  14. 31 0
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/IngestUserAgentPluginBridge.java
  15. 31 0
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/RedactPluginBridge.java
  16. 15 12
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/script/MetadataBridge.java
  17. 49 7
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/script/ScriptServiceBridge.java
  18. 11 4
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/script/TemplateScriptBridge.java
  19. 8 5
      libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/threadpool/ThreadPoolBridge.java
  20. 2 0
      modules/ingest-user-agent/src/main/java/module-info.java
  21. 22 0
      x-pack/plugin/mapper-constant-keyword/src/main/java/module-info.java
  22. 18 0
      x-pack/plugin/redact/src/main/java/module-info.java
  23. 32 0
      x-pack/plugin/spatial/src/main/java/module-info.java
  24. 23 0
      x-pack/plugin/wildcard/src/main/java/module-info.java

+ 7 - 1
libs/logstash-bridge/build.gradle

@@ -17,7 +17,13 @@ dependencies {
   compileOnly project(':modules:lang-painless:spi')
   compileOnly project(':modules:lang-mustache')
   compileOnly project(':modules:ingest-common')
-//  compileOnly project(':modules:ingest-geoip')
+  compileOnly project(':modules:ingest-geoip')
+  compileOnly project(':modules:ingest-user-agent')
+  compileOnly project(':x-pack:plugin:core')
+  compileOnly project(':x-pack:plugin:mapper-constant-keyword')
+  compileOnly project(':x-pack:plugin:redact')
+  compileOnly project(':x-pack:plugin:spatial')
+  compileOnly project(':x-pack:plugin:wildcard')
 }
 
 tasks.named('forbiddenApisMain').configure {

+ 7 - 0
libs/logstash-bridge/src/main/java/module-info.java

@@ -14,8 +14,15 @@ module org.elasticsearch.logstashbridge {
     requires org.elasticsearch.server;
     requires org.elasticsearch.painless;
     requires org.elasticsearch.painless.spi;
+    requires org.elasticsearch.ingest.common;
+    requires org.elasticsearch.ingest.useragent;
     requires org.elasticsearch.mustache;
     requires org.elasticsearch.xcontent;
+    requires org.elasticsearch.xcore;
+    requires org.elasticsearch.constantkeyword;
+    requires org.elasticsearch.redact;
+    requires org.elasticsearch.spatial;
+    requires org.elasticsearch.wildcard;
 
     exports org.elasticsearch.logstashbridge;
     exports org.elasticsearch.logstashbridge.common;

+ 24 - 18
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/StableBridgeAPI.java

@@ -16,46 +16,52 @@ import java.util.stream.Collectors;
 /**
  * A {@code StableBridgeAPI} is the stable bridge to an Elasticsearch API, and can produce instances
  * from the actual API that they mirror. As part of the LogstashBridge project, these classes are relied
- * upon by the "Elastic Integration Filter Plugin" for Logstash and their external shapes mut not change
+ * upon by the "Elastic Integration Filter Plugin" for Logstash and their external shapes must not change
  * without coordination with the maintainers of that project.
  *
- * @param <T> the actual type of the Elasticsearch API being mirrored
+ * @param <INTERNAL> the actual type of the Elasticsearch API being mirrored
  */
-public interface StableBridgeAPI<T> {
-    T unwrap();
+public interface StableBridgeAPI<INTERNAL> {
+    INTERNAL toInternal();
 
-    static <T> T unwrapNullable(final StableBridgeAPI<T> nullableStableBridgeAPI) {
+    static <T> T toInternalNullable(final StableBridgeAPI<T> nullableStableBridgeAPI) {
         if (Objects.isNull(nullableStableBridgeAPI)) {
             return null;
         }
-        return nullableStableBridgeAPI.unwrap();
+        return nullableStableBridgeAPI.toInternal();
     }
 
-    static <K, T> Map<K, T> unwrap(final Map<K, ? extends StableBridgeAPI<T>> bridgeMap) {
-        return bridgeMap.entrySet().stream().collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> e.getValue().unwrap()));
+    static <K, T> Map<K, T> toInternal(final Map<K, ? extends StableBridgeAPI<T>> bridgeMap) {
+        return bridgeMap.entrySet().stream().collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> e.getValue().toInternal()));
     }
 
-    static <K, T, B extends StableBridgeAPI<T>> Map<K, B> wrap(final Map<K, T> rawMap, final Function<T, B> wrapFunction) {
-        return rawMap.entrySet().stream().collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> wrapFunction.apply(e.getValue())));
+    static <K, T, B extends StableBridgeAPI<T>> Map<K, B> fromInternal(final Map<K, T> rawMap, final Function<T, B> externalizor) {
+        return rawMap.entrySet().stream().collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> externalizor.apply(e.getValue())));
     }
 
-    static <T, B extends StableBridgeAPI<T>> B wrap(final T delegate, final Function<T, B> wrapFunction) {
+    static <T, B extends StableBridgeAPI<T>> B fromInternal(final T delegate, final Function<T, B> externalizor) {
         if (Objects.isNull(delegate)) {
             return null;
         }
-        return wrapFunction.apply(delegate);
+        return externalizor.apply(delegate);
     }
 
-    abstract class Proxy<T> implements StableBridgeAPI<T> {
-        protected final T delegate;
+    /**
+     * An {@code ProxyInternal<INTERNAL>} is an implementation of {@code StableBridgeAPI<INTERNAL>} that
+     * proxies calls to a delegate that is an actual {@code INTERNAL}.
+     *
+     * @param <INTERNAL>
+     */
+    abstract class ProxyInternal<INTERNAL> implements StableBridgeAPI<INTERNAL> {
+        protected final INTERNAL internalDelegate;
 
-        protected Proxy(final T delegate) {
-            this.delegate = delegate;
+        protected ProxyInternal(final INTERNAL internalDelegate) {
+            this.internalDelegate = internalDelegate;
         }
 
         @Override
-        public T unwrap() {
-            return delegate;
+        public INTERNAL toInternal() {
+            return internalDelegate;
         }
     }
 }

+ 15 - 9
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/common/SettingsBridge.java

@@ -11,14 +11,17 @@ package org.elasticsearch.logstashbridge.common;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.logstashbridge.StableBridgeAPI;
 
-public class SettingsBridge extends StableBridgeAPI.Proxy<Settings> {
+/**
+ * An external bridge for {@link Settings}
+ */
+public class SettingsBridge extends StableBridgeAPI.ProxyInternal<Settings> {
 
-    public static SettingsBridge wrap(final Settings delegate) {
+    public static SettingsBridge fromInternal(final Settings delegate) {
         return new SettingsBridge(delegate);
     }
 
     public static Builder builder() {
-        return Builder.wrap(Settings.builder());
+        return Builder.fromInternal(Settings.builder());
     }
 
     public SettingsBridge(final Settings delegate) {
@@ -26,12 +29,15 @@ public class SettingsBridge extends StableBridgeAPI.Proxy<Settings> {
     }
 
     @Override
-    public Settings unwrap() {
-        return this.delegate;
+    public Settings toInternal() {
+        return this.internalDelegate;
     }
 
-    public static class Builder extends StableBridgeAPI.Proxy<Settings.Builder> {
-        static Builder wrap(final Settings.Builder delegate) {
+    /**
+     * An external bridge for {@link Settings.Builder} that proxies calls to a real {@link Settings.Builder}
+     */
+    public static class Builder extends StableBridgeAPI.ProxyInternal<Settings.Builder> {
+        static Builder fromInternal(final Settings.Builder delegate) {
             return new Builder(delegate);
         }
 
@@ -40,12 +46,12 @@ public class SettingsBridge extends StableBridgeAPI.Proxy<Settings> {
         }
 
         public Builder put(final String key, final String value) {
-            this.delegate.put(key, value);
+            this.internalDelegate.put(key, value);
             return this;
         }
 
         public SettingsBridge build() {
-            return new SettingsBridge(this.delegate.build());
+            return new SettingsBridge(this.internalDelegate.build());
         }
     }
 }

+ 3 - 0
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/core/IOUtilsBridge.java

@@ -12,6 +12,9 @@ import org.elasticsearch.core.IOUtils;
 
 import java.io.Closeable;
 
+/**
+ * An external bridge for {@link IOUtils}
+ */
 public class IOUtilsBridge {
     public static void closeWhileHandlingException(final Iterable<? extends Closeable> objects) {
         IOUtils.closeWhileHandlingException(objects);

+ 8 - 5
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/env/EnvironmentBridge.java

@@ -14,13 +14,16 @@ import org.elasticsearch.logstashbridge.common.SettingsBridge;
 
 import java.nio.file.Path;
 
-public class EnvironmentBridge extends StableBridgeAPI.Proxy<Environment> {
-    public static EnvironmentBridge wrap(final Environment delegate) {
+/**
+ * An external bridge for {@link Environment}
+ */
+public class EnvironmentBridge extends StableBridgeAPI.ProxyInternal<Environment> {
+    public static EnvironmentBridge fromInternal(final Environment delegate) {
         return new EnvironmentBridge(delegate);
     }
 
     public EnvironmentBridge(final SettingsBridge settingsBridge, final Path configPath) {
-        this(new Environment(settingsBridge.unwrap(), configPath));
+        this(new Environment(settingsBridge.toInternal(), configPath));
     }
 
     private EnvironmentBridge(final Environment delegate) {
@@ -28,7 +31,7 @@ public class EnvironmentBridge extends StableBridgeAPI.Proxy<Environment> {
     }
 
     @Override
-    public Environment unwrap() {
-        return this.delegate;
+    public Environment toInternal() {
+        return this.internalDelegate;
     }
 }

+ 4 - 1
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/ConfigurationUtilsBridge.java

@@ -14,6 +14,9 @@ import org.elasticsearch.logstashbridge.script.TemplateScriptBridge;
 
 import java.util.Map;
 
+/**
+ * An external bridge for {@link ConfigurationUtils}
+ */
 public class ConfigurationUtilsBridge {
     public static TemplateScriptBridge.Factory compileTemplate(
         final String processorType,
@@ -23,7 +26,7 @@ public class ConfigurationUtilsBridge {
         final ScriptServiceBridge scriptServiceBridge
     ) {
         return new TemplateScriptBridge.Factory(
-            ConfigurationUtils.compileTemplate(processorType, processorTag, propertyName, propertyValue, scriptServiceBridge.unwrap())
+            ConfigurationUtils.compileTemplate(processorType, processorTag, propertyName, propertyValue, scriptServiceBridge.toInternal())
         );
     }
 

+ 26 - 16
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/IngestDocumentBridge.java

@@ -18,9 +18,18 @@ import java.util.Map;
 import java.util.Set;
 import java.util.function.BiConsumer;
 
-public class IngestDocumentBridge extends StableBridgeAPI.Proxy<IngestDocument> {
+/**
+ * An external bridge for {@link IngestDocument} that proxies calls through a real {@link IngestDocument}
+ */
+public class IngestDocumentBridge extends StableBridgeAPI.ProxyInternal<IngestDocument> {
+
+    public static final class Constants {
+        public static final String METADATA_VERSION_FIELD_NAME = IngestDocument.Metadata.VERSION.getFieldName();
+
+        private Constants() {}
+    }
 
-    public static IngestDocumentBridge wrap(final IngestDocument ingestDocument) {
+    public static IngestDocumentBridge fromInternalNullable(final IngestDocument ingestDocument) {
         if (ingestDocument == null) {
             return null;
         }
@@ -36,55 +45,56 @@ public class IngestDocumentBridge extends StableBridgeAPI.Proxy<IngestDocument>
     }
 
     public MetadataBridge getMetadata() {
-        return new MetadataBridge(delegate.getMetadata());
+        return new MetadataBridge(internalDelegate.getMetadata());
     }
 
     public Map<String, Object> getSource() {
-        return delegate.getSource();
+        return internalDelegate.getSource();
     }
 
     public boolean updateIndexHistory(final String index) {
-        return delegate.updateIndexHistory(index);
+        return internalDelegate.updateIndexHistory(index);
     }
 
     public Set<String> getIndexHistory() {
-        return Set.copyOf(delegate.getIndexHistory());
+        return Set.copyOf(internalDelegate.getIndexHistory());
     }
 
     public boolean isReroute() {
-        return LogstashInternalBridge.isReroute(delegate);
+        return LogstashInternalBridge.isReroute(internalDelegate);
     }
 
     public void resetReroute() {
-        LogstashInternalBridge.resetReroute(delegate);
+        LogstashInternalBridge.resetReroute(internalDelegate);
     }
 
     public Map<String, Object> getIngestMetadata() {
-        return Map.copyOf(delegate.getIngestMetadata());
+        return internalDelegate.getIngestMetadata();
     }
 
     public <T> T getFieldValue(final String fieldName, final Class<T> type) {
-        return delegate.getFieldValue(fieldName, type);
+        return internalDelegate.getFieldValue(fieldName, type);
     }
 
     public <T> T getFieldValue(final String fieldName, final Class<T> type, final boolean ignoreMissing) {
-        return delegate.getFieldValue(fieldName, type, ignoreMissing);
+        return internalDelegate.getFieldValue(fieldName, type, ignoreMissing);
     }
 
     public String renderTemplate(final TemplateScriptBridge.Factory templateScriptFactory) {
-        return delegate.renderTemplate(templateScriptFactory.unwrap());
+        return internalDelegate.renderTemplate(templateScriptFactory.toInternal());
     }
 
     public void setFieldValue(final String path, final Object value) {
-        delegate.setFieldValue(path, value);
+        internalDelegate.setFieldValue(path, value);
     }
 
     public void removeField(final String path) {
-        delegate.removeField(path);
+        internalDelegate.removeField(path);
     }
 
-    // public void executePipeline(Pipeline pipeline, BiConsumer<IngestDocument, Exception> handler) {
     public void executePipeline(final PipelineBridge pipelineBridge, final BiConsumer<IngestDocumentBridge, Exception> handler) {
-        this.delegate.executePipeline(pipelineBridge.unwrap(), (unwrapped, e) -> handler.accept(IngestDocumentBridge.wrap(unwrapped), e));
+        this.internalDelegate.executePipeline(pipelineBridge.toInternal(), (ingestDocument, e) -> {
+            handler.accept(IngestDocumentBridge.fromInternalNullable(ingestDocument), e);
+        });
     }
 }

+ 12 - 9
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/PipelineBridge.java

@@ -16,8 +16,11 @@ import org.elasticsearch.logstashbridge.script.ScriptServiceBridge;
 import java.util.Map;
 import java.util.function.BiConsumer;
 
-public class PipelineBridge extends StableBridgeAPI.Proxy<Pipeline> {
-    public static PipelineBridge wrap(final Pipeline pipeline) {
+/**
+ * An external bridge for {@link Pipeline}
+ */
+public class PipelineBridge extends StableBridgeAPI.ProxyInternal<Pipeline> {
+    public static PipelineBridge fromInternal(final Pipeline pipeline) {
         return new PipelineBridge(pipeline);
     }
 
@@ -28,12 +31,12 @@ public class PipelineBridge extends StableBridgeAPI.Proxy<Pipeline> {
         Map<String, ProcessorBridge.Factory> processorFactories,
         ScriptServiceBridge scriptServiceBridge
     ) throws Exception {
-        return wrap(
+        return fromInternal(
             Pipeline.create(
                 id,
                 config,
-                StableBridgeAPI.unwrap(processorFactories),
-                StableBridgeAPI.unwrapNullable(scriptServiceBridge),
+                StableBridgeAPI.toInternal(processorFactories),
+                StableBridgeAPI.toInternalNullable(scriptServiceBridge),
                 null
             )
         );
@@ -44,13 +47,13 @@ public class PipelineBridge extends StableBridgeAPI.Proxy<Pipeline> {
     }
 
     public String getId() {
-        return delegate.getId();
+        return internalDelegate.getId();
     }
 
     public void execute(final IngestDocumentBridge ingestDocumentBridge, final BiConsumer<IngestDocumentBridge, Exception> handler) {
-        this.delegate.execute(
-            StableBridgeAPI.unwrapNullable(ingestDocumentBridge),
-            (unwrapped, e) -> handler.accept(IngestDocumentBridge.wrap(unwrapped), e)
+        this.internalDelegate.execute(
+            StableBridgeAPI.toInternalNullable(ingestDocumentBridge),
+            (ingestDocument, e) -> handler.accept(IngestDocumentBridge.fromInternalNullable(ingestDocument), e)
         );
     }
 }

+ 10 - 7
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/PipelineConfigurationBridge.java

@@ -15,7 +15,10 @@ import org.elasticsearch.xcontent.XContentType;
 
 import java.util.Map;
 
-public class PipelineConfigurationBridge extends StableBridgeAPI.Proxy<PipelineConfiguration> {
+/**
+ * An external bridge for {@link PipelineConfiguration}
+ */
+public class PipelineConfigurationBridge extends StableBridgeAPI.ProxyInternal<PipelineConfiguration> {
     public PipelineConfigurationBridge(final PipelineConfiguration delegate) {
         super(delegate);
     }
@@ -25,25 +28,25 @@ public class PipelineConfigurationBridge extends StableBridgeAPI.Proxy<PipelineC
     }
 
     public String getId() {
-        return delegate.getId();
+        return internalDelegate.getId();
     }
 
     public Map<String, Object> getConfig() {
-        return delegate.getConfig();
+        return internalDelegate.getConfig();
     }
 
     public Map<String, Object> getConfig(final boolean unmodifiable) {
-        return delegate.getConfig(unmodifiable);
+        return internalDelegate.getConfig(unmodifiable);
     }
 
     @Override
     public int hashCode() {
-        return delegate.hashCode();
+        return internalDelegate.hashCode();
     }
 
     @Override
     public String toString() {
-        return delegate.toString();
+        return internalDelegate.toString();
     }
 
     @Override
@@ -51,7 +54,7 @@ public class PipelineConfigurationBridge extends StableBridgeAPI.Proxy<PipelineC
         if (this == obj) {
             return true;
         } else if (obj instanceof PipelineConfigurationBridge other) {
-            return delegate.equals(other.delegate);
+            return internalDelegate.equals(other.internalDelegate);
         } else {
             return false;
         }

+ 108 - 36
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/ingest/ProcessorBridge.java

@@ -8,8 +8,10 @@
  */
 package org.elasticsearch.logstashbridge.ingest;
 
+import org.elasticsearch.cluster.metadata.ProjectId;
 import org.elasticsearch.core.FixForMultiProject;
 import org.elasticsearch.core.TimeValue;
+import org.elasticsearch.ingest.IngestDocument;
 import org.elasticsearch.ingest.IngestService;
 import org.elasticsearch.ingest.Processor;
 import org.elasticsearch.logstashbridge.StableBridgeAPI;
@@ -20,7 +22,11 @@ import org.elasticsearch.logstashbridge.threadpool.ThreadPoolBridge;
 import java.util.Map;
 import java.util.function.BiConsumer;
 
+/**
+ * An external bridge for {@link Processor}
+ */
 public interface ProcessorBridge extends StableBridgeAPI<Processor> {
+
     String getType();
 
     String getTag();
@@ -29,48 +35,108 @@ public interface ProcessorBridge extends StableBridgeAPI<Processor> {
 
     boolean isAsync();
 
-    void execute(IngestDocumentBridge ingestDocumentBridge, BiConsumer<IngestDocumentBridge, Exception> handler) throws Exception;
+    void execute(IngestDocumentBridge ingestDocumentBridge, BiConsumer<IngestDocumentBridge, Exception> handler);
+
+    static ProcessorBridge fromInternal(final Processor internalProcessor) {
+        if (internalProcessor instanceof AbstractExternal.ProxyExternal externalProxy) {
+            return externalProxy.getProcessorBridge();
+        }
+        return new ProxyInternal(internalProcessor);
+    }
+
+    /**
+     * The {@code ProcessorBridge.AbstractExternal} is an abstract base class for implementing
+     * the {@link ProcessorBridge} externally to the Elasticsearch code-base. It takes care of
+     * the details of maintaining a singular internal-form implementation of {@link Processor}
+     * that proxies calls through the external implementation.
+     */
+    abstract class AbstractExternal implements ProcessorBridge {
+        private ProxyExternal internalProcessor;
+
+        public Processor toInternal() {
+            if (internalProcessor == null) {
+                internalProcessor = new ProxyExternal();
+            }
+            return internalProcessor;
+        }
+
+        private class ProxyExternal implements Processor {
 
-    static ProcessorBridge wrap(final Processor delegate) {
-        return new Wrapped(delegate);
+            @Override
+            public String getType() {
+                return AbstractExternal.this.getType();
+            }
+
+            @Override
+            public String getTag() {
+                return AbstractExternal.this.getTag();
+            }
+
+            @Override
+            public String getDescription() {
+                return AbstractExternal.this.getDescription();
+            }
+
+            @Override
+            public void execute(IngestDocument ingestDocument, BiConsumer<IngestDocument, Exception> handler) {
+                AbstractExternal.this.execute(
+                    IngestDocumentBridge.fromInternalNullable(ingestDocument),
+                    (idb, e) -> handler.accept(idb.toInternal(), e)
+                );
+            }
+
+            @Override
+            public boolean isAsync() {
+                return AbstractExternal.this.isAsync();
+            }
+
+            private AbstractExternal getProcessorBridge() {
+                return AbstractExternal.this;
+            }
+        }
     }
 
-    class Wrapped extends StableBridgeAPI.Proxy<Processor> implements ProcessorBridge {
-        public Wrapped(final Processor delegate) {
+    /**
+     * An implementation of {@link ProcessorBridge} that proxies to an internal {@link Processor}
+     */
+    class ProxyInternal extends StableBridgeAPI.ProxyInternal<Processor> implements ProcessorBridge {
+        public ProxyInternal(final Processor delegate) {
             super(delegate);
         }
 
         @Override
         public String getType() {
-            return unwrap().getType();
+            return toInternal().getType();
         }
 
         @Override
         public String getTag() {
-            return unwrap().getTag();
+            return toInternal().getTag();
         }
 
         @Override
         public String getDescription() {
-            return unwrap().getDescription();
+            return toInternal().getDescription();
         }
 
         @Override
         public boolean isAsync() {
-            return unwrap().isAsync();
+            return toInternal().isAsync();
         }
 
         @Override
-        public void execute(final IngestDocumentBridge ingestDocumentBridge, final BiConsumer<IngestDocumentBridge, Exception> handler)
-            throws Exception {
-            delegate.execute(
-                StableBridgeAPI.unwrapNullable(ingestDocumentBridge),
-                (id, e) -> handler.accept(IngestDocumentBridge.wrap(id), e)
+        public void execute(final IngestDocumentBridge ingestDocumentBridge, final BiConsumer<IngestDocumentBridge, Exception> handler) {
+            internalDelegate.execute(
+                StableBridgeAPI.toInternalNullable(ingestDocumentBridge),
+                (id, e) -> handler.accept(IngestDocumentBridge.fromInternalNullable(id), e)
             );
         }
     }
 
-    class Parameters extends StableBridgeAPI.Proxy<Processor.Parameters> {
+    /**
+     * An external bridge for {@link Processor.Parameters}
+     */
+    class Parameters extends StableBridgeAPI.ProxyInternal<Processor.Parameters> {
 
         public Parameters(
             final EnvironmentBridge environmentBridge,
@@ -79,17 +145,17 @@ public interface ProcessorBridge extends StableBridgeAPI<Processor> {
         ) {
             this(
                 new Processor.Parameters(
-                    environmentBridge.unwrap(),
-                    scriptServiceBridge.unwrap(),
+                    environmentBridge.toInternal(),
+                    scriptServiceBridge.toInternal(),
                     null,
-                    threadPoolBridge.unwrap().getThreadContext(),
-                    threadPoolBridge.unwrap()::relativeTimeInMillis,
-                    (delay, command) -> threadPoolBridge.unwrap()
-                        .schedule(command, TimeValue.timeValueMillis(delay), threadPoolBridge.unwrap().generic()),
+                    threadPoolBridge.toInternal().getThreadContext(),
+                    threadPoolBridge.toInternal()::relativeTimeInMillis,
+                    (delay, command) -> threadPoolBridge.toInternal()
+                        .schedule(command, TimeValue.timeValueMillis(delay), threadPoolBridge.toInternal().generic()),
                     null,
                     null,
-                    threadPoolBridge.unwrap().generic()::execute,
-                    IngestService.createGrokThreadWatchdog(environmentBridge.unwrap(), threadPoolBridge.unwrap())
+                    threadPoolBridge.toInternal().generic()::execute,
+                    IngestService.createGrokThreadWatchdog(environmentBridge.toInternal(), threadPoolBridge.toInternal())
                 )
             );
         }
@@ -99,11 +165,14 @@ public interface ProcessorBridge extends StableBridgeAPI<Processor> {
         }
 
         @Override
-        public Processor.Parameters unwrap() {
-            return this.delegate;
+        public Processor.Parameters toInternal() {
+            return this.internalDelegate;
         }
     }
 
+    /**
+     * An external bridge for {@link Processor.Factory}
+     */
     interface Factory extends StableBridgeAPI<Processor.Factory> {
         ProcessorBridge create(
             Map<String, ProcessorBridge.Factory> registry,
@@ -112,23 +181,26 @@ public interface ProcessorBridge extends StableBridgeAPI<Processor> {
             Map<String, Object> config
         ) throws Exception;
 
-        static Factory wrap(final Processor.Factory delegate) {
-            return new Wrapped(delegate);
+        static Factory fromInternal(final Processor.Factory delegate) {
+            return new ProxyInternal(delegate);
         }
 
         @Override
-        default Processor.Factory unwrap() {
+        default Processor.Factory toInternal() {
             final Factory stableAPIFactory = this;
             return (registry, tag, description, config, projectId) -> stableAPIFactory.create(
-                StableBridgeAPI.wrap(registry, Factory::wrap),
+                StableBridgeAPI.fromInternal(registry, Factory::fromInternal),
                 tag,
                 description,
                 config
-            ).unwrap();
+            ).toInternal();
         }
 
-        class Wrapped extends StableBridgeAPI.Proxy<Processor.Factory> implements Factory {
-            private Wrapped(final Processor.Factory delegate) {
+        /**
+         * An implementation of {@link ProcessorBridge.Factory} that proxies to an internal {@link Processor.Factory}
+         */
+        class ProxyInternal extends StableBridgeAPI.ProxyInternal<Processor.Factory> implements Factory {
+            private ProxyInternal(final Processor.Factory delegate) {
                 super(delegate);
             }
 
@@ -140,14 +212,14 @@ public interface ProcessorBridge extends StableBridgeAPI<Processor> {
                 final String description,
                 final Map<String, Object> config
             ) throws Exception {
-                return ProcessorBridge.wrap(
-                    this.delegate.create(StableBridgeAPI.unwrap(registry), processorTag, description, config, null)
+                return ProcessorBridge.fromInternal(
+                    this.internalDelegate.create(StableBridgeAPI.toInternal(registry), processorTag, description, config, ProjectId.DEFAULT)
                 );
             }
 
             @Override
-            public Processor.Factory unwrap() {
-                return this.delegate;
+            public Processor.Factory toInternal() {
+                return this.internalDelegate;
             }
         }
     }

+ 65 - 0
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/IngestCommonPluginBridge.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+package org.elasticsearch.logstashbridge.plugins;
+
+import org.elasticsearch.ingest.common.IngestCommonPlugin;
+import org.elasticsearch.logstashbridge.StableBridgeAPI;
+import org.elasticsearch.logstashbridge.ingest.ProcessorBridge;
+
+import java.util.Map;
+
+/**
+ * An external bridge for {@link IngestCommonPlugin}
+ */
+public class IngestCommonPluginBridge implements IngestPluginBridge {
+
+    public static final String APPEND_PROCESSOR_TYPE = org.elasticsearch.ingest.common.AppendProcessor.TYPE;
+    public static final String BYTES_PROCESSOR_TYPE = org.elasticsearch.ingest.common.BytesProcessor.TYPE;
+    public static final String COMMUNITY_ID_PROCESSOR_TYPE = org.elasticsearch.ingest.common.CommunityIdProcessor.TYPE;
+    public static final String CONVERT_PROCESSOR_TYPE = org.elasticsearch.ingest.common.ConvertProcessor.TYPE;
+    public static final String CSV_PROCESSOR_TYPE = org.elasticsearch.ingest.common.CsvProcessor.TYPE;
+    public static final String DATE_INDEX_NAME_PROCESSOR_TYPE = org.elasticsearch.ingest.common.DateIndexNameProcessor.TYPE;
+    public static final String DATE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.DateProcessor.TYPE;
+    public static final String DISSECT_PROCESSOR_TYPE = org.elasticsearch.ingest.common.DissectProcessor.TYPE;
+    public static final String DROP_PROCESSOR_TYPE = org.elasticsearch.ingest.DropProcessor.TYPE;
+    public static final String FAIL_PROCESSOR_TYPE = org.elasticsearch.ingest.common.FailProcessor.TYPE;
+    public static final String FINGERPRINT_PROCESSOR_TYPE = org.elasticsearch.ingest.common.FingerprintProcessor.TYPE;
+    public static final String FOR_EACH_PROCESSOR_TYPE = org.elasticsearch.ingest.common.ForEachProcessor.TYPE;
+    public static final String GROK_PROCESSOR_TYPE = org.elasticsearch.ingest.common.GrokProcessor.TYPE;
+    public static final String GSUB_PROCESSOR_TYPE = org.elasticsearch.ingest.common.GsubProcessor.TYPE;
+    public static final String HTML_STRIP_PROCESSOR_TYPE = org.elasticsearch.ingest.common.HtmlStripProcessor.TYPE;
+    public static final String JOIN_PROCESSOR_TYPE = org.elasticsearch.ingest.common.JoinProcessor.TYPE;
+    public static final String JSON_PROCESSOR_TYPE = org.elasticsearch.ingest.common.JsonProcessor.TYPE;
+    public static final String KEY_VALUE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.KeyValueProcessor.TYPE;
+    public static final String LOWERCASE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.LowercaseProcessor.TYPE;
+    public static final String NETWORK_DIRECTION_PROCESSOR_TYPE = org.elasticsearch.ingest.common.NetworkDirectionProcessor.TYPE;
+    public static final String REGISTERED_DOMAIN_PROCESSOR_TYPE = org.elasticsearch.ingest.common.RegisteredDomainProcessor.TYPE;
+    public static final String REMOVE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.RemoveProcessor.TYPE;
+    public static final String RENAME_PROCESSOR_TYPE = org.elasticsearch.ingest.common.RenameProcessor.TYPE;
+    public static final String REROUTE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.RerouteProcessor.TYPE;
+    public static final String SCRIPT_PROCESSOR_TYPE = org.elasticsearch.ingest.common.ScriptProcessor.TYPE;
+    public static final String SET_PROCESSOR_TYPE = org.elasticsearch.ingest.common.SetProcessor.TYPE;
+    public static final String SORT_PROCESSOR_TYPE = org.elasticsearch.ingest.common.SortProcessor.TYPE;
+    public static final String SPLIT_PROCESSOR_TYPE = org.elasticsearch.ingest.common.SplitProcessor.TYPE;
+    public static final String TRIM_PROCESSOR_TYPE = org.elasticsearch.ingest.common.TrimProcessor.TYPE;
+    public static final String URL_DECODE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.URLDecodeProcessor.TYPE;
+    public static final String UPPERCASE_PROCESSOR_TYPE = org.elasticsearch.ingest.common.UppercaseProcessor.TYPE;
+    public static final String URI_PARTS_PROCESSOR_TYPE = org.elasticsearch.ingest.common.UriPartsProcessor.TYPE;
+
+    private final IngestCommonPlugin delegate;
+
+    public IngestCommonPluginBridge() {
+        delegate = new IngestCommonPlugin();
+    }
+
+    @Override
+    public Map<String, ProcessorBridge.Factory> getProcessors(final ProcessorBridge.Parameters parameters) {
+        return StableBridgeAPI.fromInternal(this.delegate.getProcessors(parameters.toInternal()), ProcessorBridge.Factory::fromInternal);
+    }
+}

+ 17 - 8
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/IngestPluginBridge.java

@@ -16,31 +16,40 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.util.Map;
 
+/**
+ * An external bridge for {@link IngestPlugin}
+ */
 public interface IngestPluginBridge {
     Map<String, ProcessorBridge.Factory> getProcessors(ProcessorBridge.Parameters parameters);
 
-    static Wrapped wrap(final IngestPlugin delegate) {
-        return new Wrapped(delegate);
+    static ProxyInternal fromInternal(final IngestPlugin delegate) {
+        return new ProxyInternal(delegate);
     }
 
-    class Wrapped extends StableBridgeAPI.Proxy<IngestPlugin> implements IngestPluginBridge, Closeable {
+    /**
+     * An implementation of {@link IngestPluginBridge} that proxies calls to an internal {@link IngestPlugin}
+     */
+    class ProxyInternal extends StableBridgeAPI.ProxyInternal<IngestPlugin> implements IngestPluginBridge, Closeable {
 
-        private Wrapped(final IngestPlugin delegate) {
+        private ProxyInternal(final IngestPlugin delegate) {
             super(delegate);
         }
 
         public Map<String, ProcessorBridge.Factory> getProcessors(final ProcessorBridge.Parameters parameters) {
-            return StableBridgeAPI.wrap(this.delegate.getProcessors(parameters.unwrap()), ProcessorBridge.Factory::wrap);
+            return StableBridgeAPI.fromInternal(
+                this.internalDelegate.getProcessors(parameters.toInternal()),
+                ProcessorBridge.Factory::fromInternal
+            );
         }
 
         @Override
-        public IngestPlugin unwrap() {
-            return this.delegate;
+        public IngestPlugin toInternal() {
+            return this.internalDelegate;
         }
 
         @Override
         public void close() throws IOException {
-            if (this.delegate instanceof Closeable closeableDelegate) {
+            if (this.internalDelegate instanceof Closeable closeableDelegate) {
                 closeableDelegate.close();
             }
         }

+ 31 - 0
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/IngestUserAgentPluginBridge.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+package org.elasticsearch.logstashbridge.plugins;
+
+import org.elasticsearch.ingest.useragent.IngestUserAgentPlugin;
+import org.elasticsearch.logstashbridge.StableBridgeAPI;
+import org.elasticsearch.logstashbridge.ingest.ProcessorBridge;
+
+import java.util.Map;
+
+/**
+ * An external bridge for {@link IngestUserAgentPlugin}
+ */
+public class IngestUserAgentPluginBridge implements IngestPluginBridge {
+
+    private final IngestUserAgentPlugin delegate;
+
+    public IngestUserAgentPluginBridge() {
+        delegate = new IngestUserAgentPlugin();
+    }
+
+    public Map<String, ProcessorBridge.Factory> getProcessors(final ProcessorBridge.Parameters parameters) {
+        return StableBridgeAPI.fromInternal(this.delegate.getProcessors(parameters.toInternal()), ProcessorBridge.Factory::fromInternal);
+    }
+}

+ 31 - 0
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/plugins/RedactPluginBridge.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+package org.elasticsearch.logstashbridge.plugins;
+
+import org.elasticsearch.license.XPackLicenseState;
+import org.elasticsearch.logstashbridge.ingest.ProcessorBridge;
+import org.elasticsearch.xpack.redact.RedactProcessor;
+
+import java.util.Map;
+
+/**
+ * An external bridge for {@link org.elasticsearch.xpack.redact.RedactPlugin}
+ */
+public class RedactPluginBridge implements IngestPluginBridge {
+    @Override
+    public Map<String, ProcessorBridge.Factory> getProcessors(ProcessorBridge.Parameters parameters) {
+        // Provide a TRIAL license state to the redact processor
+        final XPackLicenseState trialLicenseState = new XPackLicenseState(parameters.toInternal().relativeTimeSupplier);
+
+        return Map.of(
+            RedactProcessor.TYPE,
+            ProcessorBridge.Factory.fromInternal(new RedactProcessor.Factory(trialLicenseState, parameters.toInternal().matcherWatchdog))
+        );
+    }
+}

+ 15 - 12
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/script/MetadataBridge.java

@@ -13,52 +13,55 @@ import org.elasticsearch.script.Metadata;
 
 import java.time.ZonedDateTime;
 
-public class MetadataBridge extends StableBridgeAPI.Proxy<Metadata> {
+/**
+ * An external bridge for {@link Metadata}
+ */
+public class MetadataBridge extends StableBridgeAPI.ProxyInternal<Metadata> {
     public MetadataBridge(final Metadata delegate) {
         super(delegate);
     }
 
     public String getIndex() {
-        return delegate.getIndex();
+        return internalDelegate.getIndex();
     }
 
     public void setIndex(final String index) {
-        delegate.setIndex(index);
+        internalDelegate.setIndex(index);
     }
 
     public String getId() {
-        return delegate.getId();
+        return internalDelegate.getId();
     }
 
     public void setId(final String id) {
-        delegate.setId(id);
+        internalDelegate.setId(id);
     }
 
     public long getVersion() {
-        return delegate.getVersion();
+        return internalDelegate.getVersion();
     }
 
     public void setVersion(final long version) {
-        delegate.setVersion(version);
+        internalDelegate.setVersion(version);
     }
 
     public String getVersionType() {
-        return delegate.getVersionType();
+        return internalDelegate.getVersionType();
     }
 
     public void setVersionType(final String versionType) {
-        delegate.setVersionType(versionType);
+        internalDelegate.setVersionType(versionType);
     }
 
     public String getRouting() {
-        return delegate.getRouting();
+        return internalDelegate.getRouting();
     }
 
     public void setRouting(final String routing) {
-        delegate.setRouting(routing);
+        internalDelegate.setRouting(routing);
     }
 
     public ZonedDateTime getNow() {
-        return delegate.getNow();
+        return internalDelegate.getNow();
     }
 }

+ 49 - 7
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/script/ScriptServiceBridge.java

@@ -9,11 +9,14 @@
 package org.elasticsearch.logstashbridge.script;
 
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.ingest.common.ProcessorsWhitelistExtension;
 import org.elasticsearch.logstashbridge.StableBridgeAPI;
 import org.elasticsearch.logstashbridge.common.SettingsBridge;
 import org.elasticsearch.painless.PainlessPlugin;
 import org.elasticsearch.painless.PainlessScriptEngine;
+import org.elasticsearch.painless.spi.PainlessExtension;
 import org.elasticsearch.painless.spi.Whitelist;
+import org.elasticsearch.plugins.ExtensiblePlugin;
 import org.elasticsearch.script.IngestConditionalScript;
 import org.elasticsearch.script.IngestScript;
 import org.elasticsearch.script.ScriptContext;
@@ -21,27 +24,35 @@ import org.elasticsearch.script.ScriptEngine;
 import org.elasticsearch.script.ScriptModule;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.script.mustache.MustacheScriptEngine;
+import org.elasticsearch.xpack.constantkeyword.ConstantKeywordPainlessExtension;
+import org.elasticsearch.xpack.spatial.SpatialPainlessExtension;
+import org.elasticsearch.xpack.wildcard.WildcardPainlessExtension;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.LongSupplier;
 
-public class ScriptServiceBridge extends StableBridgeAPI.Proxy<ScriptService> implements Closeable {
-    public ScriptServiceBridge wrap(final ScriptService delegate) {
+/**
+ * An external bridge for {@link ScriptService}
+ */
+public class ScriptServiceBridge extends StableBridgeAPI.ProxyInternal<ScriptService> implements Closeable {
+    public ScriptServiceBridge fromInternal(final ScriptService delegate) {
         return new ScriptServiceBridge(delegate);
     }
 
-    public ScriptServiceBridge(final SettingsBridge settingsBridge, final LongSupplier timeProvider) {
-        super(getScriptService(settingsBridge.unwrap(), timeProvider));
+    public ScriptServiceBridge(final SettingsBridge settingsBridge, final LongSupplier timeProvider) throws IOException {
+        super(getScriptService(settingsBridge.toInternal(), timeProvider));
     }
 
     public ScriptServiceBridge(ScriptService delegate) {
         super(delegate);
     }
 
-    private static ScriptService getScriptService(final Settings settings, final LongSupplier timeProvider) {
+    private static ScriptService getScriptService(final Settings settings, final LongSupplier timeProvider) throws IOException {
         final List<Whitelist> painlessBaseWhitelist = getPainlessBaseWhiteList();
         final Map<ScriptContext<?>, List<Whitelist>> scriptContexts = Map.of(
             IngestScript.CONTEXT,
@@ -51,7 +62,7 @@ public class ScriptServiceBridge extends StableBridgeAPI.Proxy<ScriptService> im
         );
         final Map<String, ScriptEngine> scriptEngines = Map.of(
             PainlessScriptEngine.NAME,
-            new PainlessScriptEngine(settings, scriptContexts),
+            getPainlessScriptEngine(settings),
             MustacheScriptEngine.NAME,
             new MustacheScriptEngine(settings)
         );
@@ -62,8 +73,39 @@ public class ScriptServiceBridge extends StableBridgeAPI.Proxy<ScriptService> im
         return PainlessPlugin.baseWhiteList();
     }
 
+    /**
+     * @param settings the Elasticsearch settings object
+     * @return a {@link ScriptEngine} for painless scripts for use in {@link IngestScript} and
+     *         {@link IngestConditionalScript} contexts, including all available {@link PainlessExtension}s.
+     * @throws IOException when the underlying script engine cannot be created
+     */
+    private static ScriptEngine getPainlessScriptEngine(final Settings settings) throws IOException {
+        try (PainlessPlugin painlessPlugin = new PainlessPlugin()) {
+            painlessPlugin.loadExtensions(new ExtensiblePlugin.ExtensionLoader() {
+                @Override
+                @SuppressWarnings("unchecked")
+                public <T> List<T> loadExtensions(Class<T> extensionPointType) {
+                    if (extensionPointType.isAssignableFrom(PainlessExtension.class)) {
+                        final List<PainlessExtension> extensions = new ArrayList<>();
+
+                        extensions.add(new ConstantKeywordPainlessExtension());  // module: constant-keyword
+                        extensions.add(new ProcessorsWhitelistExtension());      // module: ingest-common
+                        extensions.add(new SpatialPainlessExtension());          // module: spatial
+                        extensions.add(new WildcardPainlessExtension());         // module: wildcard
+
+                        return (List<T>) extensions;
+                    } else {
+                        return List.of();
+                    }
+                }
+            });
+
+            return painlessPlugin.getScriptEngine(settings, Set.of(IngestScript.CONTEXT, IngestConditionalScript.CONTEXT));
+        }
+    }
+
     @Override
     public void close() throws IOException {
-        this.delegate.close();
+        this.internalDelegate.close();
     }
 }

+ 11 - 4
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/script/TemplateScriptBridge.java

@@ -11,9 +11,16 @@ package org.elasticsearch.logstashbridge.script;
 import org.elasticsearch.logstashbridge.StableBridgeAPI;
 import org.elasticsearch.script.TemplateScript;
 
+/**
+ * An external bridge for {@link TemplateScript}
+ */
 public class TemplateScriptBridge {
-    public static class Factory extends StableBridgeAPI.Proxy<TemplateScript.Factory> {
-        public static Factory wrap(final TemplateScript.Factory delegate) {
+
+    /**
+     * An external bridge for {@link TemplateScript.Factory}
+     */
+    public static class Factory extends StableBridgeAPI.ProxyInternal<TemplateScript.Factory> {
+        public static Factory fromInternal(final TemplateScript.Factory delegate) {
             return new Factory(delegate);
         }
 
@@ -22,8 +29,8 @@ public class TemplateScriptBridge {
         }
 
         @Override
-        public TemplateScript.Factory unwrap() {
-            return this.delegate;
+        public TemplateScript.Factory toInternal() {
+            return this.internalDelegate;
         }
     }
 }

+ 8 - 5
libs/logstash-bridge/src/main/java/org/elasticsearch/logstashbridge/threadpool/ThreadPoolBridge.java

@@ -16,10 +16,13 @@ import org.elasticsearch.threadpool.ThreadPool;
 
 import java.util.concurrent.TimeUnit;
 
-public class ThreadPoolBridge extends StableBridgeAPI.Proxy<ThreadPool> {
+/**
+ * An external bridge for {@link ThreadPool}
+ */
+public class ThreadPoolBridge extends StableBridgeAPI.ProxyInternal<ThreadPool> {
 
     public ThreadPoolBridge(final SettingsBridge settingsBridge) {
-        this(new ThreadPool(settingsBridge.unwrap(), MeterRegistry.NOOP, new DefaultBuiltInExecutorBuilders()));
+        this(new ThreadPool(settingsBridge.toInternal(), MeterRegistry.NOOP, new DefaultBuiltInExecutorBuilders()));
     }
 
     public ThreadPoolBridge(final ThreadPool delegate) {
@@ -27,14 +30,14 @@ public class ThreadPoolBridge extends StableBridgeAPI.Proxy<ThreadPool> {
     }
 
     public static boolean terminate(final ThreadPoolBridge pool, final long timeout, final TimeUnit timeUnit) {
-        return ThreadPool.terminate(pool.unwrap(), timeout, timeUnit);
+        return ThreadPool.terminate(pool.toInternal(), timeout, timeUnit);
     }
 
     public long relativeTimeInMillis() {
-        return delegate.relativeTimeInMillis();
+        return internalDelegate.relativeTimeInMillis();
     }
 
     public long absoluteTimeInMillis() {
-        return delegate.absoluteTimeInMillis();
+        return internalDelegate.absoluteTimeInMillis();
     }
 }

+ 2 - 0
modules/ingest-user-agent/src/main/java/module-info.java

@@ -11,4 +11,6 @@ module org.elasticsearch.ingest.useragent {
     requires org.elasticsearch.server;
     requires org.elasticsearch.xcontent;
     requires org.elasticsearch.base;
+
+    exports org.elasticsearch.ingest.useragent;
 }

+ 22 - 0
x-pack/plugin/mapper-constant-keyword/src/main/java/module-info.java

@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/** Elasticsearch X-Pack Constant Keyword Mapper Plugin. */
+module org.elasticsearch.constantkeyword {
+    requires org.elasticsearch.base;
+    requires org.elasticsearch.server;
+    requires org.elasticsearch.painless.spi;
+    requires org.elasticsearch.xcontent;
+    requires org.elasticsearch.xcore;
+    requires org.apache.lucene.core;
+
+    exports org.elasticsearch.xpack.constantkeyword;
+
+    opens org.elasticsearch.xpack.constantkeyword to org.elasticsearch.painless.spi;
+
+    provides org.elasticsearch.painless.spi.PainlessExtension with org.elasticsearch.xpack.constantkeyword.ConstantKeywordPainlessExtension;
+}

+ 18 - 0
x-pack/plugin/redact/src/main/java/module-info.java

@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/** Elasticsearch Redact Plugin. */
+module org.elasticsearch.redact {
+    requires org.elasticsearch.base;
+    requires org.elasticsearch.server;
+    requires org.elasticsearch.grok;
+    requires org.elasticsearch.xcore;
+    requires org.apache.logging.log4j;
+    requires org.jruby.joni;
+
+    exports org.elasticsearch.xpack.redact;
+}

+ 32 - 0
x-pack/plugin/spatial/src/main/java/module-info.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/** Elasticsearch X-Pack Spatial Plugin. */
+module org.elasticsearch.spatial {
+    requires org.elasticsearch.base;
+    requires org.elasticsearch.geo;
+    requires org.elasticsearch.h3;
+    requires org.elasticsearch.server;
+    requires org.elasticsearch.painless.spi;
+    requires org.elasticsearch.xcore;
+    requires org.elasticsearch.xcontent;
+    requires org.elasticsearch.legacy.geo;
+    requires org.apache.lucene.core;
+    requires org.apache.lucene.spatial3d;
+
+    exports org.elasticsearch.xpack.spatial;
+    exports org.elasticsearch.xpack.spatial.action;
+    exports org.elasticsearch.xpack.spatial.common;
+    exports org.elasticsearch.xpack.spatial.search.aggregations.bucket.geogrid;
+    exports org.elasticsearch.xpack.spatial.index.fielddata;
+    exports org.elasticsearch.xpack.spatial.index.fielddata.plain;
+    exports org.elasticsearch.xpack.spatial.index.mapper;
+
+    opens org.elasticsearch.xpack.spatial to org.elasticsearch.painless.spi;
+
+    provides org.elasticsearch.painless.spi.PainlessExtension with org.elasticsearch.xpack.spatial.SpatialPainlessExtension;
+}

+ 23 - 0
x-pack/plugin/wildcard/src/main/java/module-info.java

@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/** Elasticsearch X-Pack Wildcard Plugin. */
+module org.elasticsearch.wildcard {
+    requires org.elasticsearch.base;
+    requires org.elasticsearch.server;
+    requires org.elasticsearch.painless.spi;
+    requires org.elasticsearch.xcontent;
+    requires org.elasticsearch.xcore;
+    requires org.apache.lucene.core;
+    requires org.apache.lucene.analysis.common;
+
+    exports org.elasticsearch.xpack.wildcard;
+
+    opens org.elasticsearch.xpack.wildcard to org.elasticsearch.painless.spi;
+
+    provides org.elasticsearch.painless.spi.PainlessExtension with org.elasticsearch.xpack.wildcard.WildcardPainlessExtension;
+}