Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor dagger mains for better extensibility #3159

Merged
merged 7 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/
package io.deephaven.python.server;

import dagger.BindsInstance;
import dagger.Component;
import io.deephaven.configuration.Configuration;
import io.deephaven.engine.util.ScriptSession;
Expand All @@ -12,22 +11,21 @@
import io.deephaven.io.log.LogLevel;
import io.deephaven.io.logger.LogBuffer;
import io.deephaven.io.logger.LogBufferOutputStream;
import io.deephaven.server.auth.AuthorizationProvider;
import io.deephaven.server.auth.CommunityAuthorizationProvider;
import io.deephaven.server.auth.CommunityAuthorizationModule;
import io.deephaven.server.console.SessionToExecutionStateModule;
import io.deephaven.server.console.groovy.GroovyConsoleSessionModule;
import io.deephaven.server.console.python.PythonConsoleSessionModule;
import io.deephaven.server.console.python.PythonGlobalScopeModule;
import io.deephaven.server.healthcheck.HealthCheckModule;
import io.deephaven.server.jetty.JettyConfig;
import io.deephaven.server.jetty.JettyConfig.Builder;
import io.deephaven.server.jetty.JettyServerComponent;
import io.deephaven.server.jetty.JettyServerModule;
import io.deephaven.server.plugin.python.PythonPluginsRegistration;
import io.deephaven.server.runner.DeephavenApiConfigModule;
import io.deephaven.server.runner.DeephavenApiServer;
import io.deephaven.server.runner.DeephavenApiServerComponent;
import io.deephaven.server.runner.DeephavenApiServerModule;
import io.deephaven.server.runner.Main;
import io.deephaven.server.runner.MainHelper;
import io.deephaven.server.util.Scheduler;
import org.jpy.PyModule;
import org.jpy.PyObject;
Expand All @@ -41,6 +39,7 @@
import java.io.PrintStream;

public class EmbeddedServer {

@Singleton
@Component(modules = {
DeephavenApiServerModule.class,
Expand All @@ -54,17 +53,11 @@ public class EmbeddedServer {
PythonConsoleSessionModule.class,
GroovyConsoleSessionModule.class,
SessionToExecutionStateModule.class,
CommunityAuthorizationModule.class,
})
public interface PythonServerComponent extends DeephavenApiServerComponent {
public interface PythonServerComponent extends JettyServerComponent {
@Component.Builder
interface Builder extends DeephavenApiServerComponent.Builder<PythonServerComponent.Builder> {
@BindsInstance
Builder withJettyConfig(JettyConfig config);

@BindsInstance
Builder withAuthorizationProvider(AuthorizationProvider authorizationProvider);

PythonServerComponent build();
interface Builder extends JettyServerComponent.Builder<Builder, PythonServerComponent> {
}

void injectFields(EmbeddedServer instance);
Expand All @@ -90,7 +83,7 @@ public EmbeddedServer(String host, Integer port, PyObject dict) throws IOExcepti
System.setOut(new PrintStream(new PyLogOutputStream(() -> sys.getAttribute("stdout"))));
System.setErr(new PrintStream(new PyLogOutputStream(() -> sys.getAttribute("stderr"))));

final Configuration config = Main.init(new String[0], EmbeddedServer.class);
final Configuration config = MainHelper.init(new String[0], EmbeddedServer.class);
final Builder builder = JettyConfig.buildFromConfig(config);
if (host != null) {
builder.host(host);
Expand All @@ -101,7 +94,6 @@ public EmbeddedServer(String host, Integer port, PyObject dict) throws IOExcepti
DaggerEmbeddedServer_PythonServerComponent
.builder()
.withJettyConfig(builder.build())
.withAuthorizationProvider(new CommunityAuthorizationProvider())
.withOut(null)
.withErr(null)
.build()
Expand Down
13 changes: 13 additions & 0 deletions server/jetty-app-custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Example integration

This module has been setup as an example for how an integrator can customize the structure and configuration of the
Deephaven server.

It is not meant to be an exhaustive example of Deephaven configuration points, nor dagger conventions, but rather as a
starting example.

## Quickstart

```shell
./gradlew server-jetty-app-custom:run -Pgroovy
```
97 changes: 97 additions & 0 deletions server/jetty-app-custom/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
plugins {
id 'application'
id 'io.deephaven.project.register'
}

configurations {
applicationDist
}

dependencies {
implementation project(':server-jetty')
Classpaths.inheritDagger(project)

runtimeOnly project(':log-to-slf4j')
runtimeOnly project(':logback-print-stream-globals')
runtimeOnly project(':logback-logbuffer')
Classpaths.inheritLogbackClassic(project)
}

distributions {
main {
distributionBaseName = 'server-jetty-custom'
}
}

def extraJvmArgs = [
'-server',
'-XshowSettings:vm',
]

if (hasProperty('groovy')) {
extraJvmArgs += ['-Ddeephaven.console.type=groovy']
}

if (!hasProperty('excludeHotspotImpl')) {
dependencies {
runtimeOnly project(':hotspot-impl')
}
extraJvmArgs += ['--add-exports', 'java.management/sun.management=ALL-UNNAMED']
}

if (!hasProperty('excludeClockImpl')) {
dependencies {
runtimeOnly project(':clock-impl')
}
extraJvmArgs += ['--add-exports', 'java.base/jdk.internal.misc=ALL-UNNAMED']
}

if (hasProperty('devCerts') || hasProperty('devMTLS')) {
extraJvmArgs += [
'-Dhttp.port=8443',
'-Dssl.identity.type=privatekey',
'-Dssl.identity.certChainPath=../dev-certs/server.chain.crt',
'-Dssl.identity.privateKeyPath=../dev-certs/server.key',
]
if (hasProperty('devMTLS')) {
extraJvmArgs += [
'-Dssl.trust.type=certs',
'-Dssl.trust.path=../dev-certs/ca.crt',
'-Dssl.clientAuthentication=NEEDED',
]
}
}

if (hasProperty('debug')) {
extraJvmArgs += ['-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005']
}

if (hasProperty('debugAutocomplete')) {
extraJvmArgs += ['-Ddeephaven.console.autocomplete.quiet=false']
}

if (hasProperty('gcApplication')) {
extraJvmArgs += ['-Dio.deephaven.app.GcApplication.enabled=true']
}

if (hasProperty('quiet')) {
extraJvmArgs += ['-Ddeephaven.quiet=true']
}

tasks.withType(JavaExec).configureEach {
// This appends to the existing jvm args, so that java-open-nio still takes effect
jvmArgs extraJvmArgs
}

tasks.withType(CreateStartScripts).configureEach {
defaultJvmOpts += extraJvmArgs
}

applicationName = 'start'
mainClassName = 'io.deephaven.server.custom.CustomMain'

artifacts {
applicationDist project.tasks.findByName('distTar')
}

apply plugin: 'io.deephaven.java-open-nio'
1 change: 1 addition & 0 deletions server/jetty-app-custom/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.deephaven.project.ProjectType=JAVA_APPLICATION
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.server.custom;

import io.deephaven.appmode.ApplicationState;
import io.deephaven.appmode.ApplicationState.Listener;
import io.deephaven.engine.liveness.LivenessScope;
import io.deephaven.engine.liveness.LivenessScopeStack;
import io.deephaven.engine.util.TableTools;
import io.deephaven.util.SafeCloseable;

import java.util.Objects;

/**
* Simple application that creates a single-celled string table named {@value #FIELD_NAME}.
*/
public final class CustomApplication1 implements ApplicationState.Factory {
devinrsmith marked this conversation as resolved.
Show resolved Hide resolved
public static final String FIELD_NAME = "app1_value";

private final String value;
@SuppressWarnings("FieldCanBeLocal")
private LivenessScope scope;

public CustomApplication1(String value) {
this.value = Objects.requireNonNull(value);
}

@Override
public ApplicationState create(Listener appStateListener) {
final ApplicationState state = new ApplicationState(appStateListener, CustomApplication1.class.getName(),
CustomApplication1.class.getSimpleName());
scope = new LivenessScope();
try (final SafeCloseable ignored = LivenessScopeStack.open(scope, false)) {
state.setField(FIELD_NAME, TableTools.newTable(TableTools.stringCol("value", value)));
}
return state;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.server.custom;

import io.deephaven.appmode.ApplicationState;
import io.deephaven.appmode.ApplicationState.Listener;
import io.deephaven.engine.liveness.LivenessScope;
import io.deephaven.engine.liveness.LivenessScopeStack;
import io.deephaven.engine.util.TableTools;
import io.deephaven.util.SafeCloseable;

/**
* Simple application that creates a single-celled int table named {@value #FIELD_NAME}.
*/
public final class CustomApplication2 implements ApplicationState.Factory {
devinrsmith marked this conversation as resolved.
Show resolved Hide resolved
public static final String FIELD_NAME = "app2_value";

private final int value;
@SuppressWarnings("FieldCanBeLocal")
private LivenessScope scope;

public CustomApplication2(int value) {
this.value = value;
}

@Override
public ApplicationState create(Listener appStateListener) {
final ApplicationState state = new ApplicationState(appStateListener, CustomApplication2.class.getName(),
CustomApplication2.class.getSimpleName());
scope = new LivenessScope();
try (final SafeCloseable ignored = LivenessScopeStack.open(scope, false)) {
state.setField(FIELD_NAME, TableTools.newTable(TableTools.intCol("value", value)));
}
return state;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.server.custom;

import io.deephaven.auth.codegen.impl.InputTableServiceContextualAuthWiring;
import io.deephaven.server.auth.AllowAllAuthorizationProvider;
import io.deephaven.server.auth.AuthorizationProvider;
import io.deephaven.server.auth.CommunityAuthorizationProvider;

import javax.inject.Inject;

/**
* Simple authorization that "allows all" except "denys all" for the {@link #getInputTableServiceContextualAuthWiring()
* input table service}.
*/
public final class CustomAuthorization extends AllowAllAuthorizationProvider {

@Inject
public CustomAuthorization() {}

@Override
public InputTableServiceContextualAuthWiring getInputTableServiceContextualAuthWiring() {
// Disable input table service
return new InputTableServiceContextualAuthWiring.DenyAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.server.custom;

import dagger.Binds;
import dagger.BindsInstance;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
import io.deephaven.appmode.ApplicationState;
import io.deephaven.configuration.Configuration;
import io.deephaven.server.auth.AuthorizationProvider;
import io.deephaven.server.custom.CustomComponentFactory.CustomComponent;
import io.deephaven.server.jetty.JettyServerComponent;
import io.deephaven.server.jetty.JettyConfig;
import io.deephaven.server.jetty.JettyServerModule;
import io.deephaven.server.runner.CommunityDefaultsModule;
import io.deephaven.server.runner.ComponentFactoryBase;

import javax.inject.Singleton;
import java.io.PrintStream;

/**
* An example of a "custom integrator" component factory. This is not meant to be an exhaustive example of Deephaven
* configuration points, nor dagger conventions, but rather as a starting example for implementing
* {@link ComponentFactoryBase}.
*
* <p>
* {@link CustomApplication1} is configured with the configuration property {@value APP1_VALUE_PROP} if present,
* otherwise {@value APP1_VALUE_DEFAULT}.
*
* <p>
* {@link CustomApplication2} is configured with the configuration property {@value APP2_VALUE_PROP} if present,
* otherwise {@value APP2_VALUE_DEFAULT}.
*/
public final class CustomComponentFactory extends ComponentFactoryBase<CustomComponent> {

public static final String APP1_VALUE_PROP = "app1.value";
public static final String APP2_VALUE_PROP = "app2.value";
public static final String APP1_VALUE_DEFAULT = "hello, world";
public static final int APP2_VALUE_DEFAULT = 42;

@Override
public CustomComponent build(Configuration configuration, PrintStream out, PrintStream err) {
final JettyConfig jettyConfig = JettyConfig.buildFromConfig(configuration).build();
return DaggerCustomComponentFactory_CustomComponent.builder()
.withOut(out)
.withErr(err)
.withJettyConfig(jettyConfig)
// Bind CustomApplication1 directly
.withCustomApplication1(
new CustomApplication1(configuration.getStringWithDefault(APP1_VALUE_PROP, APP1_VALUE_DEFAULT)))
.build();
}

// Dagger will generate DaggerCustomComponentFactory_CustomComponent at compile time based on the annotations
// attached to CustomComponent and CustomComponent.Builder.
@Singleton
@Component(modules = CustomModule.class)
public interface CustomComponent extends JettyServerComponent {

@Component.Builder
interface Builder extends JettyServerComponent.Builder<Builder, CustomComponent> {
// Use @BindsInstance annotation for supplying CustomApplication1 directly
@BindsInstance
Builder withCustomApplication1(CustomApplication1 app1);
}
}

@Module(includes = {
JettyServerModule.class,
CommunityDefaultsModule.class,
})
public interface CustomModule {

// Use @Provides annotation for CustomApplication2
@Provides
static CustomApplication2 providesApp2() {
final int value = Configuration.getInstance().getIntegerWithDefault(APP2_VALUE_PROP, APP2_VALUE_DEFAULT);
return new CustomApplication2(value);
}

// Register CustomApplication1 as an application
@Binds
@IntoSet
ApplicationState.Factory providesApplication1(CustomApplication1 app1);

// Register CustomApplication2 as an application
@Binds
@IntoSet
ApplicationState.Factory providesApplication2(CustomApplication2 app2);

// Register CustomAuthorization as the authorization provider
@Binds
AuthorizationProvider bindsAuthorizationProvider(CustomAuthorization customAuthorization);
}
}
Loading