blob: 059163e72110eef088de66756356a658464c8258 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tinkerpop.gremlin.server;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin;
import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.server.auth.AllowAllAuthenticator;
import org.apache.tinkerpop.gremlin.server.auth.Authenticator;
import org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer;
import org.apache.tinkerpop.gremlin.server.handler.AbstractAuthenticationHandler;
import org.apache.tinkerpop.gremlin.server.util.DefaultGraphManager;
import org.apache.tinkerpop.gremlin.server.util.LifeCycleHook;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.UUID;
import javax.net.ssl.TrustManager;
/**
* Server settings as configured by a YAML file.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public class Settings {
public Settings() {
// setup some sensible defaults like gremlin-groovy
scriptEngines = new HashMap<>();
scriptEngines.put("gremlin-groovy", new ScriptEngineSettings());
}
/**
* Host to bind the server to. Defaults to localhost.
*/
public String host = "localhost";
/**
* Port to bind the server to. Defaults to 8182.
*/
public int port = 8182;
/**
* Size of the worker thread pool. Defaults to 1 and should generally not exceed 2 * number of cores. A
* worker thread performs non-blocking read and write for one or more Channels in a non-blocking mode.
*/
public int threadPoolWorker = 1;
/**
* detect if the OS is linux, then use epoll instead of NIO which causes less GC
*/
public boolean useEpollEventLoop = false;
/**
* Size of the Gremlin thread pool. This pool handles Gremlin script execution and other related "long-run"
* processing. Defaults to a setting of 0 which indicates the value should be set to
* {@code Runtime#availableProcessors()}.
*/
public int gremlinPool = 0;
/**
* Size of the boss thread pool. Defaults to 1 and should likely stay at 1. The bossy thread accepts incoming
* connections on a port until it is unbound. Once a connection is accepted successfully, the boss thread
* passes processing to the worker threads.
*/
public int threadPoolBoss = 1;
/**
* Time in milliseconds to wait for a request (script or bytecode) to complete execution. Defaults to -1 and
* thus defers to {@link #evaluationTimeout} for this configuration. When set to something greater than -1 then
* this configuration is used.
*
* @deprecated As of release 3.3.9, replaced by {@link #evaluationTimeout}.
*/
@Deprecated
public long scriptEvaluationTimeout = -1L;
/**
* Time in milliseconds to wait for a request (script or bytecode) to complete execution. Defaults to 30000.
*/
public long evaluationTimeout = 30000L;
/**
* Number of items in a particular resultset to iterate and serialize prior to pushing the data down the wire
* to the client.
*/
public int resultIterationBatchSize = 64;
/**
* The maximum length of the initial line (e.g. {@code "GET / HTTP/1.0"}) processed in a request, which essentially
* controls the maximum length of the submitted URI. This setting ties to the Netty {@code HttpRequestDecoder}.
*/
public int maxInitialLineLength = 4096;
/**
* The maximum length of all headers. This setting ties to the Netty {@code HttpRequestDecoder}
*/
public int maxHeaderSize = 8192;
/**
* The maximum length of the content or each chunk. If the content length exceeds this value, the transfer
* encoding of the decoded request will be converted to 'chunked' and the content will be split into multiple
* {@code HttpContent}s. If the transfer encoding of the HTTP request is 'chunked' already, each chunk will be
* split into smaller chunks if the length of the chunk exceeds this value.
*/
public int maxChunkSize = 8192;
/**
* The maximum length of the aggregated content for a message. Works in concert with {@link #maxChunkSize} where
* chunked requests are accumulated back into a single message. A request exceeding this size will
* return a 413 - Request Entity Too Large status code. A response exceeding this size will raise an internal
* exception.
*/
public int maxContentLength = 1024 * 64;
/**
* Maximum number of request components that can be aggregated for a message.
*/
public int maxAccumulationBufferComponents = 1024;
/**
* If the number of bytes in the network send buffer exceeds this value then the channel is no longer writeable,
* accepting no additional writes until buffer is drained and the {@link #writeBufferLowWaterMark} is met.
*/
public int writeBufferHighWaterMark = 1024 * 64;
/**
* Once the number of bytes queued in the network send buffer exceeds the high water mark, the channel will not
* become writeable again until the buffer is drained and it drops below this value.
*/
public int writeBufferLowWaterMark = 1024 * 32;
/**
* Time in milliseconds that the server will allow a channel to not receive requests from a client before it
* automatically closes. If enabled, the value provided should typically exceed the amount of time given to
* {@link #keepAliveInterval}. Note that while this value is to be provided as milliseconds it will resolve to
* second precision. Set this value to 0 to disable this feature.
*/
public long idleConnectionTimeout = 0;
/**
* Time in milliseconds that the server will allow a channel to not send responses to a client before it sends
* a "ping" to see if it is still present. If it is present, the client should respond with a "pong" which will
* thus reset the {@link #idleConnectionTimeout} and keep the channel open. If enabled, this number should be
* smaller than the value provided to the {@link #idleConnectionTimeout}. Note that while this value is to be
* provided as milliseconds it will resolve to second precision. Set this value to 0 to disable this feature.
*/
public long keepAliveInterval = 0;
/**
* If set to {@code true} the {@code aliases} option is required on requests and Gremlin Server will use that
* information to control which {@link Graph} instances are transaction managed for that request. If this
* setting is false (which is the default), specification of {@code aliases} is not required and Gremlin
* Server will simply apply a commit for all graphs. This setting only applies to sessionless requests.
* With either setting the user is responsible for their own transaction management for in-session requests.
*/
public boolean strictTransactionManagement = false;
/**
* The full class name of the {@link Channelizer} to use in Gremlin Server.
*/
public String channelizer = WebSocketChannelizer.class.getName();
/**
* The full class name of the {@link GraphManager} to use in Gremlin Server.
*/
public String graphManager = DefaultGraphManager.class.getName();
/**
* Configured metrics for Gremlin Server.
*/
public ServerMetrics metrics = null;
/**
* {@link Map} of {@link Graph} objects keyed by their binding name.
*/
public Map<String, String> graphs = new HashMap<>();
/**
* {@link Map} of settings for {@code ScriptEngine} setting objects keyed by the name of the {@code ScriptEngine}
* implementation.
*/
public Map<String, ScriptEngineSettings> scriptEngines;
/**
* List of {@link MessageSerializer} to configure. If no serializers are specified then default serializers for
* the most current versions of "application/json" and "application/vnd.gremlin-v1.0+gryo" are applied.
*/
public List<SerializerSettings> serializers = Collections.emptyList();
/**
* Configures settings for SSL.
*/
public SslSettings ssl = null;
public AuthenticationSettings authentication = new AuthenticationSettings();
/**
* Custom settings for {@link OpProcessor} implementations. Implementations are loaded via
* {@link ServiceLoader} but custom configurations can be supplied through this configuration.
*/
public List<ProcessorSettings> processors = new ArrayList<>();
/**
* Find the {@link ProcessorSettings} related to the specified class. If there are multiple entries then only the
* first is returned.
*/
public Optional<ProcessorSettings> optionalProcessor(final Class<? extends OpProcessor> clazz) {
return processors.stream()
.filter(p -> p.className.equals(clazz.getCanonicalName()))
.findFirst();
}
public Optional<ServerMetrics> optionalMetrics() {
return Optional.ofNullable(metrics);
}
public Optional<SslSettings> optionalSsl() {
return Optional.ofNullable(ssl);
}
public long getEvaluationTimeout() {
return -1 == scriptEvaluationTimeout ? evaluationTimeout : scriptEvaluationTimeout;
}
/**
* Read configuration from a file into a new {@link Settings} object.
*
* @param file the location of a Gremlin Server YAML configuration file
* @return a new {@link Optional} object wrapping the created {@link Settings}
*/
public static Settings read(final String file) throws Exception {
final InputStream input = new FileInputStream(new File(file));
return read(input);
}
/**
* Read configuration from a file into a new {@link Settings} object.
*
* @param stream an input stream containing a Gremlin Server YAML configuration
* @return a new {@link Optional} object wrapping the created {@link Settings}
*/
public static Settings read(final InputStream stream) {
Objects.requireNonNull(stream);
final Constructor constructor = new Constructor(Settings.class);
final TypeDescription settingsDescription = new TypeDescription(Settings.class);
settingsDescription.putMapPropertyType("graphs", String.class, String.class);
settingsDescription.putMapPropertyType("scriptEngines", String.class, ScriptEngineSettings.class);
settingsDescription.putListPropertyType("serializers", SerializerSettings.class);
settingsDescription.putListPropertyType("plugins", String.class);
settingsDescription.putListPropertyType("processors", ProcessorSettings.class);
constructor.addTypeDescription(settingsDescription);
final TypeDescription serializerSettingsDescription = new TypeDescription(SerializerSettings.class);
serializerSettingsDescription.putMapPropertyType("config", String.class, Object.class);
constructor.addTypeDescription(serializerSettingsDescription);
final TypeDescription scriptEngineSettingsDescription = new TypeDescription(ScriptEngineSettings.class);
scriptEngineSettingsDescription.putListPropertyType("imports", String.class);
scriptEngineSettingsDescription.putListPropertyType("staticImports", String.class);
scriptEngineSettingsDescription.putListPropertyType("scripts", String.class);
scriptEngineSettingsDescription.putMapPropertyType("config", String.class, Object.class);
scriptEngineSettingsDescription.putMapPropertyType("plugins", String.class, Object.class);
constructor.addTypeDescription(scriptEngineSettingsDescription);
final TypeDescription sslSettings = new TypeDescription(SslSettings.class);
constructor.addTypeDescription(sslSettings);
final TypeDescription authenticationSettings = new TypeDescription(AuthenticationSettings.class);
constructor.addTypeDescription(authenticationSettings);
final TypeDescription serverMetricsDescription = new TypeDescription(ServerMetrics.class);
constructor.addTypeDescription(serverMetricsDescription);
final TypeDescription consoleReporterDescription = new TypeDescription(ConsoleReporterMetrics.class);
constructor.addTypeDescription(consoleReporterDescription);
final TypeDescription csvReporterDescription = new TypeDescription(CsvReporterMetrics.class);
constructor.addTypeDescription(csvReporterDescription);
final TypeDescription jmxReporterDescription = new TypeDescription(JmxReporterMetrics.class);
constructor.addTypeDescription(jmxReporterDescription);
final TypeDescription slf4jReporterDescription = new TypeDescription(Slf4jReporterMetrics.class);
constructor.addTypeDescription(slf4jReporterDescription);
final TypeDescription gangliaReporterDescription = new TypeDescription(GangliaReporterMetrics.class);
constructor.addTypeDescription(gangliaReporterDescription);
final TypeDescription graphiteReporterDescription = new TypeDescription(GraphiteReporterMetrics.class);
constructor.addTypeDescription(graphiteReporterDescription);
final Yaml yaml = new Yaml(constructor);
return yaml.loadAs(stream, Settings.class);
}
/**
* Custom configurations for any {@link OpProcessor} implementations. These settings will not be relevant
* unless the referenced {@link OpProcessor} is actually loaded via {@link ServiceLoader}.
*/
public static class ProcessorSettings {
/**
* The fully qualified class name of an {@link OpProcessor} implementation.
*/
public String className;
/**
* A set of configurations as expected by the {@link OpProcessor}. Consult the documentation of the
* {@link OpProcessor} for information on what these configurations should be.
*/
public Map<String, Object> config;
}
/**
* Settings for the {@code ScriptEngine}.
*/
public static class ScriptEngineSettings {
/**
* A comma separated list of classes/packages to make available to the {@code ScriptEngine}.
*/
public List<String> imports = new ArrayList<>();
/**
* A comma separated list of "static" imports to make available to the {@code ScriptEngine}.
*/
public List<String> staticImports = new ArrayList<>();
/**
* A comma separated list of script files to execute on {@code ScriptEngine} initialization. {@link Graph} and
* {@link TraversalSource} instance references produced from scripts will be stored globally in Gremlin
* Server, therefore it is possible to use initialization scripts to add {@link TraversalStrategy} instances
* or create entirely new {@link Graph} instances all together. Instantiating a {@link LifeCycleHook} in a
* script provides a way to execute scripts when Gremlin Server starts and stops.
*/
public List<String> scripts = new ArrayList<>();
/**
* A Map of configuration settings for the {@code ScriptEngine}. These settings are dependent on the
* {@code ScriptEngine} implementation being used.
*/
public Map<String, Object> config = null;
/**
* A set of configurations for {@link GremlinPlugin} instances to apply to this {@link GremlinScriptEngine}.
* Plugins will be applied in the order they are listed.
*/
public Map<String,Map<String,Object>> plugins = new LinkedHashMap<>();
}
/**
* Settings for the {@link MessageSerializer} implementations.
*/
public static class SerializerSettings {
public SerializerSettings() {}
SerializerSettings(final String className, final Map<String, Object> config) {
this.className = className;
this.config = config;
}
/**
* The fully qualified class name of the {@link MessageSerializer} implementation. This class name will be
* used to load the implementation from the classpath.
*/
public String className;
/**
* A {@link Map} containing {@link MessageSerializer} specific configurations. Consult the
* {@link MessageSerializer} implementation for specifics on what configurations are expected.
*/
public Map<String, Object> config = Collections.emptyMap();
}
/**
* Settings for the {@link Authenticator} implementation.
*/
public static class AuthenticationSettings {
/**
* The fully qualified class name of the {@link Authenticator} implementation. This class name will be
* used to load the implementation from the classpath. Defaults to {@link AllowAllAuthenticator} when
* not specified.
*/
public String authenticator = AllowAllAuthenticator.class.getName();
/**
* The fully qualified class name of the {@link AbstractAuthenticationHandler} implementation.
* This class name will be used to load the implementation from the classpath.
* Defaults to null when not specified.
*/
public String authenticationHandler = null;
/**
* Enable audit logging of authenticated users and gremlin evaluation requests.
*/
public boolean enableAuditLog = false;
/**
* A {@link Map} containing {@link Authenticator} specific configurations. Consult the
* {@link Authenticator} implementation for specifics on what configurations are expected.
*/
public Map<String, Object> config = null;
}
/**
* Settings to configure SSL support.
*/
public static class SslSettings {
/**
* Enables SSL. Other SSL settings will be ignored unless this is set to true.
*/
public boolean enabled = false;
/**
* The X.509 certificate chain file in PEM format.
*
* @deprecated As of release 3.2.10, replaced by {@link #keyStore}
*/
@Deprecated
public String keyCertChainFile = null;
/**
* The PKCS#8 private key file in PEM format.
*
* @deprecated As of release 3.2.10, replaced by {@link #keyStore}
*/
@Deprecated
public String keyFile = null;
/**
* The password of the {@link #keyFile}, or {@code null} if it's not
* password-protected.
*
* @deprecated As of release 3.2.10, replaced by {@link #keyStorePassword}
*/
@Deprecated
public String keyPassword = null;
/**
* Trusted certificates for verifying the remote endpoint's certificate. The
* file should contain an X.509 certificate chain in PEM format. {@code null}
* uses the system default.
*
* @deprecated As of release 3.2.10, replaced by {@link #trustStore}
*/
@Deprecated
public String trustCertChainFile = null;
/**
* The file location of the private key in JKS or PKCS#12 format.
*/
public String keyStore;
/**
* The password of the {@link #keyStore}, or {@code null} if it's not password-protected.
*/
public String keyStorePassword;
/**
* Trusted certificates for verifying the remote client's certificate. If
* this value is not provided and SSL is enabled, the default {@link TrustManager} will be used.
*/
public String trustStore;
/**
* The password of the {@link #trustStore}, or {@code null} if it's not password-protected.
*/
public String trustStorePassword;
/**
* The format of the {@link #keyStore}, either {@code JKS} or {@code PKCS12}
*/
public String keyStoreType;
/**
* The format of the {@link #trustStore}, either {@code JKS} or {@code PKCS12}
*/
public String trustStoreType;
/**
* A list of SSL protocols to enable. @see <a href=
* "https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SunJSSE_Protocols">JSSE
* Protocols</a>
*/
public List<String> sslEnabledProtocols = new ArrayList<>();
/**
* A list of cipher suites to enable. @see <a href=
* "https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SupportedCipherSuites">Cipher
* Suites</a>
*/
public List<String> sslCipherSuites = new ArrayList<>();
/**
* Require client certificate authentication
*/
public ClientAuth needClientAuth = ClientAuth.NONE;
private SslContext sslContext;
/**
* When this value is set, the other settings for SSL are ignored. This option provides for a programmatic
* way to configure more complex SSL configurations. The {@link #enabled} setting should still be set to
* {@code true} for this setting to take effect.
*/
public void overrideSslContext(final SslContext sslContext) {
this.sslContext = sslContext;
}
public Optional<SslContext> getSslContext() {
return Optional.ofNullable(sslContext);
}
}
/**
* Settings for {@code Metrics} recorded by Gremlin Server.
*/
public static class ServerMetrics {
public ConsoleReporterMetrics consoleReporter = null;
public CsvReporterMetrics csvReporter = null;
public JmxReporterMetrics jmxReporter = null;
public Slf4jReporterMetrics slf4jReporter = null;
public GangliaReporterMetrics gangliaReporter = null;
public GraphiteReporterMetrics graphiteReporter = null;
public Optional<ConsoleReporterMetrics> optionalConsoleReporter() {
return Optional.ofNullable(consoleReporter);
}
public Optional<CsvReporterMetrics> optionalCsvReporter() {
return Optional.ofNullable(csvReporter);
}
public Optional<JmxReporterMetrics> optionalJmxReporter() {
return Optional.ofNullable(jmxReporter);
}
public Optional<Slf4jReporterMetrics> optionalSlf4jReporter() {
return Optional.ofNullable(slf4jReporter);
}
public Optional<GangliaReporterMetrics> optionalGangliaReporter() {
return Optional.ofNullable(gangliaReporter);
}
public Optional<GraphiteReporterMetrics> optionalGraphiteReporter() {
return Optional.ofNullable(graphiteReporter);
}
}
/**
* Settings for a {@code Metrics} reporter that writes to the console.
*/
public static class ConsoleReporterMetrics extends IntervalMetrics {
}
/**
* Settings for a {@code Metrics} reporter that writes to a CSV file.
*/
public static class CsvReporterMetrics extends IntervalMetrics {
public String fileName = "metrics.csv";
}
/**
* Settings for a {@code Metrics} reporter that writes to JMX.
*/
public static class JmxReporterMetrics extends BaseMetrics {
public String domain = null;
public String agentId = null;
}
/**
* Settings for a {@code Metrics} reporter that writes to the SL4J output.
*/
public static class Slf4jReporterMetrics extends IntervalMetrics {
public String loggerName = Slf4jReporterMetrics.class.getName();
}
/**
* Settings for a {@code Metrics} reporter that writes to Ganglia.
*/
public static class GangliaReporterMetrics extends HostPortIntervalMetrics {
public String addressingMode = null;
public int ttl = 1;
public boolean protocol31 = true;
public UUID hostUUID = null;
public String spoof = null;
public GangliaReporterMetrics() {
// default ganglia port
this.port = 8649;
}
}
/**
* Settings for a {@code Metrics} reporter that writes to Graphite.
*/
public static class GraphiteReporterMetrics extends HostPortIntervalMetrics {
public String prefix = "";
public GraphiteReporterMetrics() {
// default graphite port
this.port = 2003;
}
}
public static abstract class HostPortIntervalMetrics extends IntervalMetrics {
public String host = "localhost";
public int port;
}
public static abstract class IntervalMetrics extends BaseMetrics {
public long interval = 60000;
}
public static abstract class BaseMetrics {
public boolean enabled = false;
}
}