Skip to content
Permalink
Browse files
IGNITE-16498 Add ability to add handlers to RestModule from other Ign…
…ite modules (#690)
  • Loading branch information
rpuch committed Mar 4, 2022
1 parent 4ce4ef3 commit ed519fb2fa2a89e11a8ab7fad3069f7e1f7d74b3
Show file tree
Hide file tree
Showing 32 changed files with 518 additions and 273 deletions.
@@ -38,11 +38,6 @@
<artifactId>ignite-configuration-api</artifactId>
</dependency>

<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-configuration</artifactId>
</dependency>

<!-- 3rd party dependencies -->
<dependency>
<groupId>org.jetbrains</groupId>
@@ -43,6 +43,16 @@
<artifactId>ignite-configuration-api</artifactId>
</dependency>

<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-api</artifactId>
</dependency>

<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-rest-api</artifactId>
</dependency>

<!-- 3rd party dependencies -->
<dependency>
<groupId>com.typesafe</groupId>
@@ -15,103 +15,62 @@
* limitations under the License.
*/

package org.apache.ignite.internal.rest;
package org.apache.ignite.internal.configuration.rest;

import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.BindException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import org.apache.ignite.configuration.schemas.rest.RestConfiguration;
import org.apache.ignite.configuration.schemas.rest.RestView;
import org.apache.ignite.configuration.validation.ConfigurationValidationException;
import org.apache.ignite.internal.configuration.ConfigurationManager;
import org.apache.ignite.internal.configuration.ConfigurationRegistry;
import org.apache.ignite.internal.manager.IgniteComponent;
import org.apache.ignite.internal.rest.netty.RestApiHttpRequest;
import org.apache.ignite.internal.rest.netty.RestApiHttpResponse;
import org.apache.ignite.internal.rest.netty.RestApiInitializer;
import org.apache.ignite.internal.rest.presentation.ConfigurationPresentation;
import org.apache.ignite.internal.rest.presentation.hocon.HoconPresentation;
import org.apache.ignite.internal.rest.routes.Router;
import org.apache.ignite.internal.configuration.rest.presentation.ConfigurationPresentation;
import org.apache.ignite.internal.configuration.rest.presentation.hocon.HoconPresentation;
import org.apache.ignite.internal.rest.api.ErrorResult;
import org.apache.ignite.internal.rest.api.RestApiHttpRequest;
import org.apache.ignite.internal.rest.api.RestApiHttpResponse;
import org.apache.ignite.internal.rest.api.RestHandlersRegister;
import org.apache.ignite.internal.rest.api.Routes;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.lang.IgniteLogger;
import org.apache.ignite.network.NettyBootstrapFactory;

/**
* Rest module is responsible for starting a REST endpoints for accessing and managing configuration.
*
* <p>It is started on port 10300 by default but it is possible to change this in configuration itself. Refer to default config file in
* resources for the example.
* HTTP handlers for configuration-related REST endpoints.
*/
public class RestModule implements IgniteComponent {
/** Default port. */
public static final int DFLT_PORT = 10300;

/** Node configuration route. */
public class ConfigurationHttpHandlers {
/**
* Node configuration route.
*/
private static final String NODE_CFG_URL = "/management/v1/configuration/node/";

/** Cluster configuration route. */
/**
* Cluster configuration route.
*/
private static final String CLUSTER_CFG_URL = "/management/v1/configuration/cluster/";

/** Path parameter. */
private static final String PATH_PARAM = "selector";

/** Ignite logger. */
private final IgniteLogger log = IgniteLogger.forClass(RestModule.class);

/** Node configuration register. */
private final ConfigurationRegistry nodeCfgRegistry;

/** Presentation of node configuration. */
private final ConfigurationPresentation<String> nodeCfgPresentation;

/** Presentation of cluster configuration. */
private final ConfigurationPresentation<String> clusterCfgPresentation;

/** Netty bootstrap factory. */
private final NettyBootstrapFactory bootstrapFactory;

/** Netty channel. */
private volatile Channel channel;

/**
* Creates a new instance of REST module.
*
* @param nodeCfgMgr Node configuration manager.
* @param clusterCfgMgr Cluster configuration manager.
* @param bootstrapFactory Netty bootstrap factory.
* Path parameter.
*/
public RestModule(
ConfigurationManager nodeCfgMgr,
ConfigurationManager clusterCfgMgr,
NettyBootstrapFactory bootstrapFactory) {
nodeCfgRegistry = nodeCfgMgr.configurationRegistry();
private static final String PATH_PARAM = "selector";

nodeCfgPresentation = new HoconPresentation(nodeCfgMgr.configurationRegistry());
clusterCfgPresentation = new HoconPresentation(clusterCfgMgr.configurationRegistry());
private final ConfigurationManager nodeConfigurationManager;
private final ConfigurationManager clusterConfigurationManager;

this.bootstrapFactory = bootstrapFactory;
public ConfigurationHttpHandlers(ConfigurationManager nodeConfigurationManager, ConfigurationManager clusterConfigurationManager) {
this.nodeConfigurationManager = nodeConfigurationManager;
this.clusterConfigurationManager = clusterConfigurationManager;
}

/** {@inheritDoc} */
@Override
public void start() {
if (channel != null) {
throw new IgniteException("RestModule is already started.");
}
public void registerHandlers(RestHandlersRegister register) {
register.registerHandlers(this::registerOn);
}

var router = new Router();
private void registerOn(Routes routes) {
var nodeCfgPresentation = new HoconPresentation(nodeConfigurationManager.configurationRegistry());
var clusterCfgPresentation = new HoconPresentation(clusterConfigurationManager.configurationRegistry());

router
routes
.get(
NODE_CFG_URL,
(req, resp) -> {
@@ -138,77 +97,14 @@ public void start() {
)
.patch(
NODE_CFG_URL,
APPLICATION_JSON,
APPLICATION_JSON.toString(),
(req, resp) -> handleUpdate(req, resp, nodeCfgPresentation)
)
.patch(
CLUSTER_CFG_URL,
APPLICATION_JSON,
APPLICATION_JSON.toString(),
(req, resp) -> handleUpdate(req, resp, clusterCfgPresentation)
);

channel = startRestEndpoint(router).channel();
}

/**
* Start endpoint.
*
* @param router Dispatcher of http requests.
* @return Future which will be notified when this channel is closed.
* @throws RuntimeException if this module cannot be bound to a port.
*/
private ChannelFuture startRestEndpoint(Router router) {
RestView restConfigurationView = nodeCfgRegistry.getConfiguration(RestConfiguration.KEY).value();

int desiredPort = restConfigurationView.port();
int portRange = restConfigurationView.portRange();

int port = 0;

Channel ch = null;

var hnd = new RestApiInitializer(router);

ServerBootstrap b = bootstrapFactory.createServerBootstrap()
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(hnd);

for (int portCandidate = desiredPort; portCandidate <= desiredPort + portRange; portCandidate++) {
ChannelFuture bindRes = b.bind(portCandidate).awaitUninterruptibly();

if (bindRes.isSuccess()) {
ch = bindRes.channel();
port = portCandidate;
break;
} else if (!(bindRes.cause() instanceof BindException)) {
throw new RuntimeException(bindRes.cause());
}
}

if (ch == null) {
String msg = "Cannot start REST endpoint. "
+ "All ports in range [" + desiredPort + ", " + (desiredPort + portRange) + "] are in use.";

log.error(msg);

throw new RuntimeException(msg);
}

if (log.isInfoEnabled()) {
log.info("REST protocol started successfully on port " + port);
}

return ch.closeFuture();
}

/** {@inheritDoc} */
@Override
public void stop() throws Exception {
if (channel != null) {
channel.close().await();

channel = null;
}
}

/**
@@ -15,7 +15,7 @@
* limitations under the License.
*/

package org.apache.ignite.internal.rest.presentation;
package org.apache.ignite.internal.configuration.rest.presentation;

import java.util.concurrent.CompletableFuture;
import org.apache.ignite.configuration.validation.ConfigurationValidationException;
@@ -15,7 +15,7 @@
* limitations under the License.
*/

package org.apache.ignite.internal.rest.presentation.hocon;
package org.apache.ignite.internal.configuration.rest.presentation.hocon;

import static com.typesafe.config.ConfigFactory.parseString;
import static com.typesafe.config.ConfigRenderOptions.concise;
@@ -30,7 +30,7 @@
import org.apache.ignite.configuration.validation.ConfigurationValidationException;
import org.apache.ignite.internal.configuration.ConfigurationRegistry;
import org.apache.ignite.internal.configuration.hocon.HoconConverter;
import org.apache.ignite.internal.rest.presentation.ConfigurationPresentation;
import org.apache.ignite.internal.configuration.rest.presentation.ConfigurationPresentation;
import org.apache.ignite.lang.IgniteException;
import org.jetbrains.annotations.Nullable;

@@ -19,4 +19,4 @@
* This package contains classes related to HOCON representation.
*/

package org.apache.ignite.internal.rest.presentation.hocon;
package org.apache.ignite.internal.configuration.rest.presentation.hocon;
@@ -19,4 +19,4 @@
* Contains a base interface that provides the ability to convert representation to other formats.
*/

package org.apache.ignite.internal.rest.presentation;
package org.apache.ignite.internal.configuration.rest.presentation;
@@ -15,7 +15,7 @@
* limitations under the License.
*/

package org.apache.ignite.internal.rest.presentation;
package org.apache.ignite.internal.configuration.rest.presentation;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
@@ -42,8 +42,8 @@
import org.apache.ignite.configuration.validation.ValidationIssue;
import org.apache.ignite.configuration.validation.Validator;
import org.apache.ignite.internal.configuration.ConfigurationRegistry;
import org.apache.ignite.internal.configuration.rest.presentation.hocon.HoconPresentation;
import org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
import org.apache.ignite.internal.rest.presentation.hocon.HoconPresentation;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -43,6 +43,11 @@
<artifactId>ignite-core</artifactId>
</dependency>

<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-configuration</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>

0 comments on commit ed519fb

Please sign in to comment.