From d1b5d620dbbd1359b5391d5a40e0b19ec73d419b Mon Sep 17 00:00:00 2001 From: Alfredo Gutierrez Date: Wed, 21 Aug 2024 12:24:47 -0600 Subject: [PATCH] Initial Dagger Refactor Signed-off-by: Alfredo Gutierrez --- .../com/hedera/block/server/BlockNodeApp.java | 143 ++++++++++++++++++ .../BlockNodeAppInjectionComponent.java | 37 +++++ .../server/BlockNodeAppInjectionModule.java | 39 +++++ .../java/com/hedera/block/server/Server.java | 95 +----------- .../block/server/ServiceStatusImpl.java | 4 + .../server/health/HealthInjectionModule.java | 40 +++++ .../server/health/HealthServiceImpl.java | 4 + server/src/main/java/module-info.java | 2 + 8 files changed, 275 insertions(+), 89 deletions(-) create mode 100644 server/src/main/java/com/hedera/block/server/BlockNodeApp.java create mode 100644 server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java create mode 100644 server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java create mode 100644 server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeApp.java b/server/src/main/java/com/hedera/block/server/BlockNodeApp.java new file mode 100644 index 00000000..464c48b3 --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/BlockNodeApp.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed 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 com.hedera.block.server; + +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.config.BlockNodeContextFactory; +import com.hedera.block.server.data.ObjectEvent; +import com.hedera.block.server.health.HealthService; +import com.hedera.block.server.mediator.LiveStreamMediatorBuilder; +import com.hedera.block.server.mediator.StreamMediator; +import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; +import com.hedera.block.server.persistence.storage.read.BlockAsDirReaderBuilder; +import com.hedera.block.server.persistence.storage.read.BlockReader; +import com.hedera.block.server.persistence.storage.write.BlockAsDirWriterBuilder; +import com.hedera.block.server.persistence.storage.write.BlockWriter; +import com.hedera.block.server.producer.ItemAckBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.grpc.GrpcRouting; +import io.helidon.webserver.http.HttpRouting; +import java.io.IOException; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * The main class for the Block Node application. This class is responsible for starting the server + * and initializing the context. + */ +@Singleton +public class BlockNodeApp { + + private static final System.Logger LOGGER = System.getLogger(Server.class.getName()); + private final ServiceStatus serviceStatus; + private final HealthService healthService; + + /** + * Has all needed dependencies to start the server and initialize the context. + * + * @param serviceStatus the status of the service + * @param healthService the health service + */ + @Inject + public BlockNodeApp( + @NonNull ServiceStatus serviceStatus, @NonNull HealthService healthService) { + this.serviceStatus = serviceStatus; + this.healthService = healthService; + } + + /** + * Starts the server and binds to the specified port. + * + * @throws IOException if the server cannot be started + */ + public void startServer() throws IOException { + // init context, metrics, and configuration. + @NonNull final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + + @NonNull + final BlockWriter blockWriter = + BlockAsDirWriterBuilder.newBuilder(blockNodeContext).build(); + @NonNull + final StreamMediator< + com.hedera.block.protos.BlockStreamService.BlockItem, + ObjectEvent< + com.hedera.block.protos.BlockStreamService.SubscribeStreamResponse>> + streamMediator = + LiveStreamMediatorBuilder.newBuilder( + blockWriter, blockNodeContext, serviceStatus) + .build(); + + @NonNull + final BlockReader blockReader = + BlockAsDirReaderBuilder.newBuilder( + blockNodeContext + .configuration() + .getConfigData(PersistenceStorageConfig.class)) + .build(); + + @NonNull + final BlockStreamService blockStreamService = + buildBlockStreamService( + streamMediator, blockReader, serviceStatus, blockNodeContext); + + @NonNull + final GrpcRouting.Builder grpcRouting = GrpcRouting.builder().service(blockStreamService); + + @NonNull + final HttpRouting.Builder httpRouting = + HttpRouting.builder().register(healthService.getHealthRootPath(), healthService); + + // Build the web server + // TODO: make port server a configurable value. + @NonNull + final WebServer webServer = + WebServer.builder() + .port(8080) + .addRouting(grpcRouting) + .addRouting(httpRouting) + .build(); + + // Update the serviceStatus with the web server + serviceStatus.setWebServer(webServer); + + // Start the web server + webServer.start(); + + // Log the server status + LOGGER.log( + System.Logger.Level.INFO, "Block Node Server started at port: " + webServer.port()); + } + + @NonNull + private static BlockStreamService buildBlockStreamService( + @NonNull + final StreamMediator< + com.hedera.block.protos.BlockStreamService.BlockItem, + ObjectEvent< + com.hedera.block.protos.BlockStreamService + .SubscribeStreamResponse>> + streamMediator, + @NonNull + final BlockReader blockReader, + @NonNull final ServiceStatus serviceStatus, + @NonNull final BlockNodeContext blockNodeContext) { + + return new BlockStreamService( + new ItemAckBuilder(), streamMediator, blockReader, serviceStatus, blockNodeContext); + } +} diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java new file mode 100644 index 00000000..b429b4e8 --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed 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 com.hedera.block.server; + +import com.hedera.block.server.health.HealthInjectionModule; +import dagger.Component; +import javax.inject.Singleton; + +/** The infrastructure used to manage the instances and inject them using Dagger */ +@Singleton +@Component( + modules = { + BlockNodeAppInjectionModule.class, + HealthInjectionModule.class, + }) +public interface BlockNodeAppInjectionComponent { + /** + * Get the block node app server. + * + * @return the block node app server + */ + BlockNodeApp getBlockNodeApp(); +} diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java new file mode 100644 index 00000000..f69f4de5 --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed 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 com.hedera.block.server; + +import dagger.Binds; +import dagger.Module; +import javax.inject.Singleton; + +/** + * A Dagger Module for interfaces that are at the BlockNodeApp Level, should be temporary and + * everything should be inside its own modules. + */ +@Module +public interface BlockNodeAppInjectionModule { + + /** + * Binds the service status to the service status implementation. + * + * @param serviceStatus needs a service status implementation + * @return the service status implementation + */ + @Singleton + @Binds + ServiceStatus bindServiceStatus(ServiceStatusImpl serviceStatus); +} diff --git a/server/src/main/java/com/hedera/block/server/Server.java b/server/src/main/java/com/hedera/block/server/Server.java index a454af26..cd9e07c9 100644 --- a/server/src/main/java/com/hedera/block/server/Server.java +++ b/server/src/main/java/com/hedera/block/server/Server.java @@ -16,25 +16,7 @@ package com.hedera.block.server; -import static com.hedera.block.protos.BlockStreamService.*; - -import com.hedera.block.server.config.BlockNodeContext; -import com.hedera.block.server.config.BlockNodeContextFactory; -import com.hedera.block.server.data.ObjectEvent; -import com.hedera.block.server.health.HealthService; -import com.hedera.block.server.health.HealthServiceImpl; -import com.hedera.block.server.mediator.LiveStreamMediatorBuilder; -import com.hedera.block.server.mediator.StreamMediator; -import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; -import com.hedera.block.server.persistence.storage.read.BlockAsDirReaderBuilder; -import com.hedera.block.server.persistence.storage.read.BlockReader; -import com.hedera.block.server.persistence.storage.write.BlockAsDirWriterBuilder; -import com.hedera.block.server.persistence.storage.write.BlockWriter; -import com.hedera.block.server.producer.ItemAckBuilder; import edu.umd.cs.findbugs.annotations.NonNull; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.grpc.GrpcRouting; -import io.helidon.webserver.http.HttpRouting; import java.io.IOException; /** Main class for the block node server */ @@ -53,80 +35,15 @@ public static void main(final String[] args) { LOGGER.log(System.Logger.Level.INFO, "Starting BlockNode Server"); - try { - // init context, metrics, and configuration. - @NonNull final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); - - @NonNull final ServiceStatus serviceStatus = new ServiceStatusImpl(); - - @NonNull - final BlockWriter blockWriter = - BlockAsDirWriterBuilder.newBuilder(blockNodeContext).build(); - @NonNull - final StreamMediator> streamMediator = - LiveStreamMediatorBuilder.newBuilder( - blockWriter, blockNodeContext, serviceStatus) - .build(); - - @NonNull - final BlockReader blockReader = - BlockAsDirReaderBuilder.newBuilder( - blockNodeContext - .configuration() - .getConfigData(PersistenceStorageConfig.class)) - .build(); - - @NonNull - final BlockStreamService blockStreamService = - buildBlockStreamService( - streamMediator, blockReader, serviceStatus, blockNodeContext); - - @NonNull - final GrpcRouting.Builder grpcRouting = - GrpcRouting.builder().service(blockStreamService); - - @NonNull final HealthService healthService = new HealthServiceImpl(serviceStatus); + @NonNull + final BlockNodeAppInjectionComponent daggerComponent = + DaggerBlockNodeAppInjectionComponent.create(); + @NonNull final BlockNodeApp server = daggerComponent.getBlockNodeApp(); - @NonNull - final HttpRouting.Builder httpRouting = - HttpRouting.builder() - .register(healthService.getHealthRootPath(), healthService); - - // Build the web server - // TODO: make port server a configurable value. - @NonNull - final WebServer webServer = - WebServer.builder() - .port(8080) - .addRouting(grpcRouting) - .addRouting(httpRouting) - .build(); - - // Update the serviceStatus with the web server - serviceStatus.setWebServer(webServer); - - // Start the web server - webServer.start(); - - // Log the server status - LOGGER.log( - System.Logger.Level.INFO, - "Block Node Server started at port: " + webServer.port()); + try { + server.startServer(); } catch (IOException e) { throw new RuntimeException(e); } } - - @NonNull - private static BlockStreamService buildBlockStreamService( - @NonNull - final StreamMediator> - streamMediator, - @NonNull final BlockReader blockReader, - @NonNull final ServiceStatus serviceStatus, - @NonNull final BlockNodeContext blockNodeContext) { - - return new BlockStreamService( - new ItemAckBuilder(), streamMediator, blockReader, serviceStatus, blockNodeContext); - } } diff --git a/server/src/main/java/com/hedera/block/server/ServiceStatusImpl.java b/server/src/main/java/com/hedera/block/server/ServiceStatusImpl.java index ac9b869e..dd3243a1 100644 --- a/server/src/main/java/com/hedera/block/server/ServiceStatusImpl.java +++ b/server/src/main/java/com/hedera/block/server/ServiceStatusImpl.java @@ -19,17 +19,21 @@ import edu.umd.cs.findbugs.annotations.NonNull; import io.helidon.webserver.WebServer; import java.util.concurrent.atomic.AtomicBoolean; +import javax.inject.Inject; +import javax.inject.Singleton; /** * The ServiceStatusImpl class implements the ServiceStatus interface. It provides the * implementation for checking the status of the service and shutting down the web server. */ +@Singleton public class ServiceStatusImpl implements ServiceStatus { private final AtomicBoolean isRunning = new AtomicBoolean(true); private WebServer webServer; /** Constructor for the ServiceStatusImpl class. */ + @Inject public ServiceStatusImpl() {} /** diff --git a/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java b/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java new file mode 100644 index 00000000..db882c9b --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed 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 com.hedera.block.server.health; + +import dagger.Binds; +import dagger.Module; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Singleton; + +/** + * A Dagger module for providing dependencies for Health Module, should we refactor to have an + * observability module instead?. + */ +@Module +public interface HealthInjectionModule { + + /** + * Binds the health service to the health service implementation. + * + * @param healthService needs a health service implementation + * @return the health service implementation + */ + @Singleton + @Binds + HealthService bindHealthService(@NonNull HealthServiceImpl healthService); +} diff --git a/server/src/main/java/com/hedera/block/server/health/HealthServiceImpl.java b/server/src/main/java/com/hedera/block/server/health/HealthServiceImpl.java index 912d3996..0f622866 100644 --- a/server/src/main/java/com/hedera/block/server/health/HealthServiceImpl.java +++ b/server/src/main/java/com/hedera/block/server/health/HealthServiceImpl.java @@ -21,8 +21,11 @@ import io.helidon.webserver.http.HttpRules; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; +import javax.inject.Inject; +import javax.inject.Singleton; /** Provides implementation for the health endpoints of the server. */ +@Singleton public class HealthServiceImpl implements HealthService { private static final String LIVENESS_PATH = "/liveness"; @@ -35,6 +38,7 @@ public class HealthServiceImpl implements HealthService { * * @param serviceStatus is used to check the status of the service */ + @Inject public HealthServiceImpl(@NonNull ServiceStatus serviceStatus) { this.serviceStatus = serviceStatus; } diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 379ccf9b..95b0c83e 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -12,10 +12,12 @@ requires com.swirlds.config.api; requires com.swirlds.config.extensions; requires com.swirlds.metrics.api; + requires dagger; requires io.grpc.stub; requires io.helidon.common; requires io.helidon.webserver.grpc; requires io.helidon.webserver; + requires javax.inject; requires static com.github.spotbugs.annotations; requires static com.google.auto.service;