From 211aa0560d0bdf8f942af8210a9de59ea76ce176 Mon Sep 17 00:00:00 2001 From: Nikita Amelchev Date: Thu, 14 May 2026 11:04:33 +0300 Subject: [PATCH 1/5] IGNITE-28672 Refactor REST welcome page and resource loading --- .../http/jetty/GridJettyRestHandler.java | 145 +----------------- .../http/jetty/GridJettyRestProtocol.java | 14 +- .../protocols/http/jetty/WelcomeHandler.java | 91 +++++++++++ .../rest/protocols/http/jetty/rest.html | 79 ---------- .../ignite-rest-http}/favicon.ico | Bin .../main/resources/ignite-rest-http/logo.svg | 1 + .../main/resources/ignite-rest-http/rest.html | 122 +++++++++++++++ 7 files changed, 233 insertions(+), 219 deletions(-) create mode 100644 modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java delete mode 100644 modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html rename modules/rest-http/src/main/{java/org/apache/ignite/internal/processors/rest/protocols/http/jetty => resources/ignite-rest-http}/favicon.ico (100%) create mode 100644 modules/rest-http/src/main/resources/ignite-rest-http/logo.svg create mode 100644 modules/rest-http/src/main/resources/ignite-rest-http/rest.html diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java index efb4aff121cc9..86c58f10d424a 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java @@ -17,15 +17,9 @@ package org.apache.ignite.internal.processors.rest.protocols.http.jetty; -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.LineNumberReader; import java.io.OutputStream; import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; import java.sql.Date; import java.sql.Time; @@ -100,8 +94,8 @@ * Jetty REST handler. The following URL format is supported: {@code /ignite?cmd=cmdName¶m1=abc¶m2=123} */ public class GridJettyRestHandler extends AbstractHandler { - /** Used to sent request charset. */ - private static final String CHARSET = StandardCharsets.UTF_8.name(); + /** */ + public static final String IGNITE_CMD_PATH = "/ignite"; /** */ private static final String FAILED_TO_PARSE_FORMAT = "Failed to parse parameter of %s type [%s=%s]"; @@ -148,12 +142,6 @@ public class GridJettyRestHandler extends AbstractHandler { /** Request handlers. */ private GridRestProtocolHandler hnd; - /** Default page. */ - private volatile String dfltPage; - - /** Favicon. */ - private volatile byte[] favicon; - /** Mapper from Java object to JSON. */ private final ObjectMapper jsonMapper; @@ -175,27 +163,6 @@ public class GridJettyRestHandler extends AbstractHandler { this.authChecker = authChecker; this.log = ctx.log(getClass()); this.jsonMapper = new IgniteObjectMapper(ctx); - - // Init default page and favicon. - try { - initDefaultPage(); - - if (log.isDebugEnabled()) - log.debug("Initialized default page."); - } - catch (IOException e) { - U.warn(log, "Failed to initialize default page: " + e.getMessage()); - } - - try { - initFavicon(); - - if (log.isDebugEnabled()) - log.debug(favicon != null ? "Initialized favicon, size: " + favicon.length : "Favicon is null."); - } - catch (IOException e) { - U.warn(log, "Failed to initialize favicon: " + e.getMessage()); - } } /** @@ -302,115 +269,17 @@ private static int intValue(String key, Map params, int dfltVal) } } - /** - * @throws IOException If failed. - */ - private void initDefaultPage() throws IOException { - assert dfltPage == null; - - InputStream in = getClass().getResourceAsStream("rest.html"); - - if (in != null) { - LineNumberReader rdr = new LineNumberReader(new InputStreamReader(in, CHARSET)); - - try { - StringBuilder buf = new StringBuilder(2048); - - for (String line = rdr.readLine(); line != null; line = rdr.readLine()) { - buf.append(line); - - if (!line.endsWith(" ")) - buf.append(' '); - } - - dfltPage = buf.toString(); - } - finally { - U.closeQuiet(rdr); - } - } - } - - /** - * @throws IOException If failed. - */ - private void initFavicon() throws IOException { - assert favicon == null; - - InputStream in = getClass().getResourceAsStream("favicon.ico"); - - if (in != null) { - BufferedInputStream bis = new BufferedInputStream(in); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - try { - byte[] buf = new byte[2048]; - - while (true) { - int n = bis.read(buf); - - if (n == -1) - break; - - bos.write(buf, 0, n); - } - - favicon = bos.toByteArray(); - } - finally { - U.closeQuiet(bis); - } - } - } - /** {@inheritDoc} */ - @Override public void handle(String target, Request req, HttpServletRequest srvReq, HttpServletResponse res) - throws IOException { + @Override public void handle(String target, Request req, HttpServletRequest srvReq, HttpServletResponse res) { if (log.isDebugEnabled()) log.debug("Handling request [target=" + target + ", req=" + req + ", srvReq=" + srvReq + ']'); - if (target.startsWith("/ignite")) { - processRequest(target, srvReq, res); - - req.setHandled(true); - } - else if (target.startsWith("/favicon.ico")) { - if (favicon == null) { - res.setStatus(HttpServletResponse.SC_NOT_FOUND); - - req.setHandled(true); - - return; - } - - res.setStatus(HttpServletResponse.SC_OK); - - res.setContentType("image/x-icon"); - - res.getOutputStream().write(favicon); - res.getOutputStream().flush(); - - req.setHandled(true); - } - else { - if (dfltPage == null) { - res.setStatus(HttpServletResponse.SC_NOT_FOUND); - - req.setHandled(true); - - return; - } - - res.setStatus(HttpServletResponse.SC_OK); - - res.setContentType("text/html"); + if (!target.startsWith(IGNITE_CMD_PATH)) + return; - res.getWriter().write(dfltPage); - res.getWriter().flush(); + processRequest(target, srvReq, res); - req.setHandled(true); - } + req.setHandled(true); } /** diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java index c1d0319d88334..39ee53682d0f5 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java @@ -42,6 +42,8 @@ import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -155,9 +157,15 @@ else if (log.isDebugEnabled()) connector.setPort(port); if (startJetty()) { - if (log.isInfoEnabled()) + if (log.isInfoEnabled()) { log.info(startInfo()); + boolean isSsl = connector.getConnectionFactory(SslConnectionFactory.class) != null; + String proto = isSsl ? "https" : "http"; + + log.info("HTTP REST protocol address: " + proto + "://" + host + ":" + port + "/"); + } + return; } } @@ -312,7 +320,9 @@ private void loadJettyConfiguration(@Nullable URL cfgUrl) throws IgniteCheckedEx assert httpSrv != null; - httpSrv.setHandler(jettyHnd); + WelcomeHandler welcomeHnd = new WelcomeHandler(log); + + httpSrv.setHandler(new HandlerList(jettyHnd, welcomeHnd)); override(getJettyConnector()); } diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java new file mode 100644 index 0000000000000..8efa5fe1507cf --- /dev/null +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java @@ -0,0 +1,91 @@ +/* + * 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.ignite.internal.processors.rest.protocols.http.jetty; + +import java.io.IOException; +import java.io.InputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.ignite.IgniteLogger; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; + +/** + * Handles welcome page. + */ +public class WelcomeHandler extends AbstractHandler { + /** Default page. */ + private final byte[] dfltPage; + + /** Favicon. */ + private final byte[] favicon; + + /** Logo. */ + private final byte[] logo; + + /** */ + private final IgniteLogger log; + + /** */ + public WelcomeHandler(IgniteLogger log) { + this.log = log; + + favicon = loadResource("ignite-rest-http/favicon.ico"); + dfltPage = loadResource("ignite-rest-http/rest.html"); + logo = loadResource("ignite-rest-http/logo.svg"); + } + + /** {@inheritDoc} */ + @Override public void handle(String target, Request req, HttpServletRequest srvReq, HttpServletResponse res) throws IOException { + if (dfltPage == null || favicon == null || logo == null) { + res.setStatus(HttpServletResponse.SC_NOT_FOUND); + req.setHandled(true); + + return; + } + + if (target.startsWith("/favicon.ico")) { + res.setContentType("image/x-icon"); + res.getOutputStream().write(favicon); + } + else if (target.startsWith("/logo.svg")) { + res.setContentType("image/svg+xml"); + res.getOutputStream().write(logo); + } + else { + res.setContentType("text/html; charset=utf-8"); + res.getOutputStream().write(dfltPage); + } + + res.getOutputStream().flush(); + + res.setStatus(HttpServletResponse.SC_OK); + req.setHandled(true); + } + + /** */ + private byte[] loadResource(String path) { + try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) { + return in != null ? in.readAllBytes() : null; + } catch (IOException e) { + log.warning("Failed to load REST resource: {}", path, e); + + return null; + } + } +} diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html deleted file mode 100644 index 10c5fc1ab6f87..0000000000000 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/rest.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - Apache Ignite - In-Memory Database and Caching Platform - - - - -
-
-
- - Ignite - In-Memory Database and Caching Platform - -

-

-

REST API

- Ignite REST API supports - external connectivity to Ignite via REST over HTTP. It comes in handy whenever Ignite Java API is not - available directly, but it is still needed to execute Ignite tasks or retrieve cached data. For example, - you can conveniently use Ignite REST API over HTTP from other non-JVM languages, such as Ruby, PHP or Python, - or any other language, whenever local instance of Ignite is not available. -

- Note that PHP REST example is included with Ignite distribution. -

- All REST HTTP commands have the following format: http://1.2.3.4:8080/ignite?cmd=CMD&..., where - 'cmd' is the name of the command followed by other command parameters. Every command may have - different parameters, some of which may be mandatory and some optional. The commands parameters may be - passed either via HTTP GET or POST, whichever one is preferred. -

-
- - diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/favicon.ico b/modules/rest-http/src/main/resources/ignite-rest-http/favicon.ico similarity index 100% rename from modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/favicon.ico rename to modules/rest-http/src/main/resources/ignite-rest-http/favicon.ico diff --git a/modules/rest-http/src/main/resources/ignite-rest-http/logo.svg b/modules/rest-http/src/main/resources/ignite-rest-http/logo.svg new file mode 100644 index 0000000000000..ab96d1dc99e8d --- /dev/null +++ b/modules/rest-http/src/main/resources/ignite-rest-http/logo.svg @@ -0,0 +1 @@ + diff --git a/modules/rest-http/src/main/resources/ignite-rest-http/rest.html b/modules/rest-http/src/main/resources/ignite-rest-http/rest.html new file mode 100644 index 0000000000000..99db51ad0ae22 --- /dev/null +++ b/modules/rest-http/src/main/resources/ignite-rest-http/rest.html @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + Apache Ignite - REST API + + + + + + +
+ + +

REST API

+ +

+ Ignite REST API supports external connectivity to Ignite via REST over HTTP. + It comes in handy whenever Ignite Java API is not available directly, but it + is still needed to execute Ignite tasks or retrieve cached data. +

+ +

+ For example, you can conveniently use Ignite REST API over HTTP from other + non-JVM languages, such as Ruby, PHP, Python, or any other language, + whenever a local instance of Ignite is not available. +

+ +

+ You can find more detailed information about all available REST API commands, + parameters, and examples in the official documentation: + + Apache Ignite — REST API + . +

+ +

Note that PHP REST example is included with Ignite distribution.

+ +

+ All REST HTTP commands have the following format: + http://1.2.3.4:8080/ignite?cmd=CMD&..., where + cmd is the name of the command followed by other command parameters. +

+ +

+ Every command may have different parameters, some of which may be mandatory + and some optional. The command parameters may be passed either via HTTP GET or POST. +

+
+ + From 9ab39f1e5f2a379d82f03f5849d78b1ca2b607f0 Mon Sep 17 00:00:00 2001 From: Nikita Amelchev Date: Thu, 14 May 2026 11:25:56 +0300 Subject: [PATCH 2/5] IGNITE-28672 Refactor REST welcome page and resource loading --- .../internal/processors/rest/GridRestProcessor.java | 13 ------------- .../protocols/http/jetty/GridJettyRestHandler.java | 2 +- .../protocols/http/jetty/GridJettyRestProtocol.java | 3 --- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java index f26b3af040e2b..99e665aaa9b6c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java @@ -122,9 +122,6 @@ public class GridRestProcessor extends GridProcessorAdapter implements IgniteRes /** The default interval used to invalidate sessions, in seconds. */ public static final int DFLT_SES_TOKEN_INVALIDATE_INTERVAL = 5 * 60; - /** Index of task name wrapped by VisorGatewayTask */ - private static final int WRAPPED_TASK_IDX = 1; - /** Protocols. */ private final Collection protos = new ArrayList<>(); @@ -1104,16 +1101,6 @@ static Session fromClientId(UUID clientId) { return new Session(clientId, UUID.randomUUID()); } - /** - * Static constructor. - * - * @param sesTokId Session token ID. - * @return New session instance with random client ID and given session ID. - */ - static Session fromSessionToken(UUID sesTokId) { - return new Session(UUID.randomUUID(), sesTokId); - } - /** * Checks expiration of session and if expired then sets TIMEDOUT_FLAG. * diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java index 86c58f10d424a..673273ca2825e 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java @@ -383,7 +383,7 @@ private void processRequest(String act, HttpServletRequest req, HttpServletRespo * @return REST request. * @throws IgniteCheckedException If creation failed. */ - @Nullable private GridRestRequest createRequest( + private GridRestRequest createRequest( GridRestCommand cmd, Map params, HttpServletRequest req diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java index 39ee53682d0f5..39a47c83a7196 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestProtocol.java @@ -61,9 +61,6 @@ * Jetty REST protocol implementation. */ public class GridJettyRestProtocol extends GridRestProtocolAdapter { - /** - * - */ static { if (!IgniteSystemProperties.getBoolean(IGNITE_JETTY_LOG_NO_OVERRIDE)) { // See also https://www.eclipse.org/jetty/documentation/jetty-9/index.html#configuring-jetty-logging From 3ebd6db2b4f35cb0f5a6215b4df641c35b4656d4 Mon Sep 17 00:00:00 2001 From: Nikita Amelchev Date: Thu, 14 May 2026 11:33:19 +0300 Subject: [PATCH 3/5] IGNITE-28672 Refactor REST welcome page and resource loading --- .../rest/protocols/http/jetty/GridJettyRestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java index 673273ca2825e..333af0f951898 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java @@ -95,7 +95,7 @@ */ public class GridJettyRestHandler extends AbstractHandler { /** */ - public static final String IGNITE_CMD_PATH = "/ignite"; + private static final String IGNITE_CMD_PATH = "/ignite"; /** */ private static final String FAILED_TO_PARSE_FORMAT = "Failed to parse parameter of %s type [%s=%s]"; From 1564ca9fba9f1cfad1efb1544538e0fc8314b7a7 Mon Sep 17 00:00:00 2001 From: Nikita Amelchev Date: Fri, 15 May 2026 18:04:32 +0300 Subject: [PATCH 4/5] review fixes --- .../processors/rest/protocols/http/jetty/WelcomeHandler.java | 2 +- parent/pom.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java index 8efa5fe1507cf..bb0341216af84 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java @@ -83,7 +83,7 @@ private byte[] loadResource(String path) { try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) { return in != null ? in.readAllBytes() : null; } catch (IOException e) { - log.warning("Failed to load REST resource: {}", path, e); + log.error("Failed to load REST resource [path=" + path + ']', e); return null; } diff --git a/parent/pom.xml b/parent/pom.xml index bba4e3ac078d8..5cd61890ab5ac 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -790,6 +790,7 @@ **/*.iml **/*.csv **/*.jks + **/*.svg **/pom-installed.xml **/keystore **/keystore/*.jks From 43adff698768648abec5124ebb8c2a18147b0040 Mon Sep 17 00:00:00 2001 From: Nikita Amelchev Date: Fri, 15 May 2026 18:12:57 +0300 Subject: [PATCH 5/5] review fixes --- .../processors/rest/protocols/http/jetty/WelcomeHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java index bb0341216af84..72b162b467ab6 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/WelcomeHandler.java @@ -82,7 +82,8 @@ else if (target.startsWith("/logo.svg")) { private byte[] loadResource(String path) { try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) { return in != null ? in.readAllBytes() : null; - } catch (IOException e) { + } + catch (IOException e) { log.error("Failed to load REST resource [path=" + path + ']', e); return null;