diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeApp.java b/server/src/main/java/com/hedera/block/server/BlockNodeApp.java index 2bc644bc..5b0b8a9f 100644 --- a/server/src/main/java/com/hedera/block/server/BlockNodeApp.java +++ b/server/src/main/java/com/hedera/block/server/BlockNodeApp.java @@ -19,20 +19,10 @@ import static java.lang.System.Logger; import static java.lang.System.Logger.Level.INFO; -import com.hedera.block.server.config.BlockNodeContext; -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.BlockWriter; -import com.hedera.hapi.block.SubscribeStreamResponse; -import com.hedera.hapi.block.stream.Block; -import com.hedera.hapi.block.stream.BlockItem; import edu.umd.cs.findbugs.annotations.NonNull; import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; import io.helidon.webserver.grpc.GrpcRouting; import io.helidon.webserver.http.HttpRouting; import java.io.IOException; @@ -50,27 +40,25 @@ public class BlockNodeApp { private final ServiceStatus serviceStatus; private final HealthService healthService; - private final BlockNodeContext blockNodeContext; - private final BlockWriter blockWriter; + private final BlockStreamService blockStreamService; + private final WebServerConfig.Builder webServerBuilder; /** * Has all needed dependencies to start the server and initialize the context. * * @param serviceStatus the status of the service * @param healthService the health service - * @param blockNodeContext the context of the block node - * @param blockWriter the block writer */ @Inject public BlockNodeApp( @NonNull ServiceStatus serviceStatus, @NonNull HealthService healthService, - @NonNull BlockNodeContext blockNodeContext, - BlockWriter blockWriter) { + @NonNull BlockStreamService blockStreamService, + @NonNull WebServerConfig.Builder webServerBuilder) { this.serviceStatus = serviceStatus; this.healthService = healthService; - this.blockNodeContext = blockNodeContext; - this.blockWriter = blockWriter; + this.blockStreamService = blockStreamService; + this.webServerBuilder = webServerBuilder; } /** @@ -80,24 +68,6 @@ public BlockNodeApp( */ public void start() throws IOException { - // final BlockWriter blockWriter = - // BlockAsDirWriterBuilder.newBuilder(blockNodeContext).build(); - - final StreamMediator> streamMediator = - LiveStreamMediatorBuilder.newBuilder(blockWriter, blockNodeContext, serviceStatus) - .build(); - - final BlockReader blockReader = - BlockAsDirReaderBuilder.newBuilder( - blockNodeContext - .configuration() - .getConfigData(PersistenceStorageConfig.class)) - .build(); - - final BlockStreamService blockStreamService = - buildBlockStreamService( - streamMediator, blockReader, serviceStatus, blockNodeContext); - final GrpcRouting.Builder grpcRouting = GrpcRouting.builder().service(blockStreamService); final HttpRouting.Builder httpRouting = @@ -106,11 +76,7 @@ public void start() throws IOException { // Build the web server // TODO: make port server a configurable value. final WebServer webServer = - WebServer.builder() - .port(8080) - .addRouting(grpcRouting) - .addRouting(httpRouting) - .build(); + webServerBuilder.port(8080).addRouting(grpcRouting).addRouting(httpRouting).build(); // Update the serviceStatus with the web server serviceStatus.setWebServer(webServer); @@ -121,16 +87,4 @@ public void start() throws IOException { // Log the server status LOGGER.log(INFO, String.format("Block Node Server started at port: %d", webServer.port())); } - - @NonNull - private static BlockStreamService buildBlockStreamService( - @NonNull - final StreamMediator> - streamMediator, - @NonNull final BlockReader blockReader, - @NonNull final ServiceStatus serviceStatus, - @NonNull final BlockNodeContext blockNodeContext) { - - return new BlockStreamService(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 index 5e050eff..8f8e91a4 100644 --- a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java +++ b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java @@ -16,7 +16,9 @@ package com.hedera.block.server; +import com.hedera.block.server.config.ConfigInjectionModule; import com.hedera.block.server.health.HealthInjectionModule; +import com.hedera.block.server.mediator.MediatorInjectionModule; import com.hedera.block.server.persistence.storage.PersistenceInjectionModule; import dagger.Component; import javax.inject.Singleton; @@ -27,7 +29,9 @@ modules = { BlockNodeAppInjectionModule.class, HealthInjectionModule.class, - PersistenceInjectionModule.class + PersistenceInjectionModule.class, + MediatorInjectionModule.class, + ConfigInjectionModule.class }) public interface BlockNodeAppInjectionComponent { /** diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java index 5a5b076e..b50e16b9 100644 --- a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java +++ b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java @@ -18,9 +18,17 @@ 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.mediator.StreamMediator; +import com.hedera.block.server.persistence.storage.read.BlockReader; +import com.hedera.hapi.block.SubscribeStreamResponse; +import com.hedera.hapi.block.stream.Block; +import com.hedera.hapi.block.stream.BlockItem; import dagger.Binds; import dagger.Module; import dagger.Provides; +import edu.umd.cs.findbugs.annotations.NonNull; +import io.helidon.webserver.WebServerConfig; import java.io.IOException; import javax.inject.Singleton; @@ -46,8 +54,8 @@ public interface BlockNodeAppInjectionModule { * * @return a block node context singleton */ - @Provides @Singleton + @Provides static BlockNodeContext provideBlockNodeContext() { try { return BlockNodeContextFactory.create(); @@ -55,4 +63,22 @@ static BlockNodeContext provideBlockNodeContext() { throw new RuntimeException(e); } } + + @Singleton + @Provides + static BlockStreamService provideBlockStreamService( + @NonNull + final StreamMediator> + streamMediator, + @NonNull final BlockReader blockReader, + @NonNull final ServiceStatus serviceStatus, + @NonNull final BlockNodeContext blockNodeContext) { + return new BlockStreamService(streamMediator, blockReader, serviceStatus, blockNodeContext); + } + + @Singleton + @Provides + static WebServerConfig.Builder provideWebServerConfigBuilder() { + return WebServerConfig.builder(); + } } diff --git a/server/src/main/java/com/hedera/block/server/BlockStreamService.java b/server/src/main/java/com/hedera/block/server/BlockStreamService.java index 3b820b09..4ab2654e 100644 --- a/server/src/main/java/com/hedera/block/server/BlockStreamService.java +++ b/server/src/main/java/com/hedera/block/server/BlockStreamService.java @@ -50,6 +50,7 @@ import java.io.IOException; import java.time.Clock; import java.util.Optional; +import javax.inject.Inject; /** * The BlockStreamService class defines the gRPC service for the block stream service. It provides @@ -76,6 +77,7 @@ public class BlockStreamService implements GrpcService { * @param serviceStatus the service status provides methods to check service availability and to * stop the service and web server in the event of an unrecoverable exception */ + @Inject BlockStreamService( @NonNull final StreamMediator> diff --git a/server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java b/server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java new file mode 100644 index 00000000..6c1016bc --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java @@ -0,0 +1,86 @@ +/* + * 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.config; + +import com.hedera.block.server.consumer.ConsumerConfig; +import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; +import com.swirlds.common.config.BasicCommonConfig; +import com.swirlds.common.metrics.config.MetricsConfig; +import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; +import com.swirlds.config.api.Configuration; +import com.swirlds.config.api.ConfigurationBuilder; +import com.swirlds.config.extensions.sources.ClasspathFileConfigSource; +import com.swirlds.config.extensions.sources.SystemEnvironmentConfigSource; +import com.swirlds.config.extensions.sources.SystemPropertiesConfigSource; +import dagger.Module; +import dagger.Provides; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.IOException; +import java.nio.file.Path; +import javax.inject.Singleton; + +@Module +public interface ConfigInjectionModule { + + static final String APPLICATION_PROPERTIES = "app.properties"; + + @Singleton + @Provides + static Configuration provideConfiguration() { + try { + return ConfigurationBuilder.create() + .withSource(SystemEnvironmentConfigSource.getInstance()) + .withSource(SystemPropertiesConfigSource.getInstance()) + .withSource(new ClasspathFileConfigSource(Path.of(APPLICATION_PROPERTIES))) + .autoDiscoverExtensions() + .build(); + } catch (IOException e) { + throw new RuntimeException("Failed to create configuration", e); + } + } + + @Singleton + @Provides + static PersistenceStorageConfig providePersistenceStorageConfig( + @NonNull Configuration configuration) { + return configuration.getConfigData(PersistenceStorageConfig.class); + } + + @Singleton + @Provides + static MetricsConfig provideMetricsConfig(@NonNull Configuration configuration) { + return configuration.getConfigData(MetricsConfig.class); + } + + @Singleton + @Provides + static PrometheusConfig providePrometheusConfig(@NonNull Configuration configuration) { + return configuration.getConfigData(PrometheusConfig.class); + } + + @Singleton + @Provides + static ConsumerConfig provideConsumerConfig(@NonNull Configuration configuration) { + return configuration.getConfigData(ConsumerConfig.class); + } + + @Singleton + @Provides + static BasicCommonConfig provideBasicCommonConfig(@NonNull Configuration configuration) { + return configuration.getConfigData(BasicCommonConfig.class); + } +} diff --git a/server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java b/server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java new file mode 100644 index 00000000..f50e107b --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java @@ -0,0 +1,51 @@ +/* + * 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.mediator; + +import com.hedera.block.server.ServiceStatus; +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.data.ObjectEvent; +import com.hedera.block.server.persistence.storage.write.BlockWriter; +import com.hedera.hapi.block.SubscribeStreamResponse; +import com.hedera.hapi.block.stream.BlockItem; +import dagger.Module; +import dagger.Provides; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Singleton; + +/** A Dagger module for providing dependencies for Mediator Module.` */ +@Module +public interface MediatorInjectionModule { + + /** + * Provides the stream mediator. + * + * @param blockWriter the block writer + * @param blockNodeContext the block node context + * @param serviceStatus the service status + * @return the stream mediator + */ + @Provides + @Singleton + static StreamMediator> providesStreamMediator( + @NonNull BlockWriter blockWriter, + @NonNull BlockNodeContext blockNodeContext, + @NonNull ServiceStatus serviceStatus) { + return LiveStreamMediatorBuilder.newBuilder(blockWriter, blockNodeContext, serviceStatus) + .build(); + } +} diff --git a/server/src/main/java/com/hedera/block/server/persistence/storage/PersistenceInjectionModule.java b/server/src/main/java/com/hedera/block/server/persistence/storage/PersistenceInjectionModule.java index 5b3a940b..4669f93b 100644 --- a/server/src/main/java/com/hedera/block/server/persistence/storage/PersistenceInjectionModule.java +++ b/server/src/main/java/com/hedera/block/server/persistence/storage/PersistenceInjectionModule.java @@ -17,8 +17,11 @@ package com.hedera.block.server.persistence.storage; import com.hedera.block.server.config.BlockNodeContext; +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.hapi.block.stream.Block; import com.hedera.hapi.block.stream.BlockItem; import dagger.Module; import dagger.Provides; @@ -45,4 +48,10 @@ static BlockWriter providesBlockWriter(@NonNull BlockNodeContext bloc throw new RuntimeException("Failed to create block writer", e); } } + + @Provides + @Singleton + static BlockReader providesBlockReader(@NonNull PersistenceStorageConfig config) { + return BlockAsDirReaderBuilder.newBuilder(config).build(); + } } diff --git a/server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java b/server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java new file mode 100644 index 00000000..92f7f480 --- /dev/null +++ b/server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java @@ -0,0 +1,76 @@ +/* + * 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 static org.mockito.Mockito.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.hedera.block.server.health.HealthService; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.grpc.GrpcRouting; +import io.helidon.webserver.http.HttpRouting; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BlockNodeAppTest { + + @Mock private ServiceStatus serviceStatus; + + @Mock private HealthService healthService; + + @Mock private BlockStreamService blockStreamService; + + @Mock private WebServerConfig.Builder webServerBuilder; + + @Mock private WebServer webServer; + + @InjectMocks private BlockNodeApp blockNodeApp; + + @BeforeEach + void setup() { + when(webServerBuilder.port(8080)).thenReturn(webServerBuilder); + when(webServerBuilder.addRouting(any(GrpcRouting.Builder.class))) + .thenReturn(webServerBuilder); + when(webServerBuilder.addRouting(any(HttpRouting.Builder.class))) + .thenReturn(webServerBuilder); + when(webServerBuilder.build()).thenReturn(webServer); + when(healthService.getHealthRootPath()).thenReturn("/health"); + } + + @Test + void testStartServer() throws IOException { + // Act + blockNodeApp.start(); + + // Assert + verify(serviceStatus).setWebServer(webServer); + verify(webServer).start(); + verify(healthService).getHealthRootPath(); + verify(webServerBuilder).port(8080); + verify(webServerBuilder).addRouting(any(GrpcRouting.Builder.class)); + verify(webServerBuilder).addRouting(any(HttpRouting.Builder.class)); + verify(webServerBuilder).build(); + } +}