Skip to content
Open
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 @@ -94,8 +94,8 @@
}

@Override
public String getString(int columnIndex) throws SQLException {

Check warning on line 97 in modules/com.dbeaver.jdbc.api/src/com/dbeaver/jdbc/model/AbstractJdbcResultSet.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./modules/com.dbeaver.jdbc.api/src/com/dbeaver/jdbc/model/AbstractJdbcResultSet.java:97:12: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 97 in modules/com.dbeaver.jdbc.api/src/com/dbeaver/jdbc/model/AbstractJdbcResultSet.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./modules/com.dbeaver.jdbc.api/src/com/dbeaver/jdbc/model/AbstractJdbcResultSet.java:97:12: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
return CommonUtils.toString(getObject(columnIndex));
return CommonUtils.toString(getObject(columnIndex), null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
import java.net.InetSocketAddress;
import java.util.function.Predicate;

@FunctionalInterface

Check warning on line 26 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RequestHandlerFactory.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RequestHandlerFactory.java:26:1: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck)

Check warning on line 26 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RequestHandlerFactory.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RequestHandlerFactory.java:26:1: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck)
public interface RequestHandlerFactory<T> {
public interface RequestHandlerFactory {
@NotNull
RestServer.RequestHandler<T> createHandler(
<T> RestServer.RequestHandler<T> createHandler(
@NotNull Class<T> cls,
@NotNull T object,
@NotNull Gson gson,
Expand Down
185 changes: 153 additions & 32 deletions modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,38 +37,111 @@
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;

public class RestServer<T> {
public class RestServer {
private static final Logger log = Logger.getLogger(RestServer.class.getName());
private static final Object EMPTY_CONTROLLER = new Object();
private static final RequestHandlerFactory DEFAULT_HANDLER_FACTORY = new RequestHandlerFactory() {
@NotNull
@Override
public <T> RequestHandler<T> createHandler(
@NotNull Class<T> cls,
@NotNull T object,
@NotNull Gson gson,
@NotNull Predicate<InetSocketAddress> filter,
@Nullable String landingPage
) {
return new RequestHandler<>(cls, object, gson, filter, landingPage);
}
};

private HttpServer server;
private String landingPage;

public RestServer(
public <T> RestServer(
@NotNull Class<T> cls,
@NotNull T object,
@NotNull Gson gson,
@NotNull Predicate<InetSocketAddress> filter,
int port,
int backlog
) throws IOException {
this(cls, object, gson, filter, port, backlog, DEFAULT_HANDLER_FACTORY);
}

public <T> RestServer(
@NotNull Class<T> cls,
@NotNull T object,
@NotNull Gson gson,
@NotNull Predicate<InetSocketAddress> filter,
int port,
int backlog,
@NotNull RequestHandlerFactory<T> handlerFactory
@NotNull RequestHandlerFactory handlerFactory
) throws IOException {
this(
Collections.singletonList(new ControllerDef<>("/", cls, object)),
gson,
filter,
port,
backlog,
null,
handlerFactory
);
}

private RestServer(
@NotNull List<ControllerDef<?>> controllers,
@NotNull Gson gson,
@NotNull Predicate<InetSocketAddress> filter,
int port,
int backlog,
@Nullable String landingPage,
@NotNull RequestHandlerFactory handlerFactory
) throws IOException {
InetSocketAddress listenAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
server = HttpServer.create(listenAddr, backlog);
server.createContext("/", handlerFactory.createHandler(cls, object, gson, filter, landingPage));
boolean hasRootController = controllers.stream().anyMatch(ctrl -> "/".equals(ctrl.path));
if (controllers.isEmpty()) {
createEmptyRootContext(gson, filter, landingPage, handlerFactory);
} else {
controllers.forEach(ctrl ->
server.createContext(
ctrl.path,
createHandler(
ctrl,
gson,
filter,
"/".equals(ctrl.path) ? landingPage : null,
handlerFactory
)
)
);
if (!hasRootController && landingPage != null) {
createEmptyRootContext(gson, filter, landingPage, handlerFactory);
}
}
server.setExecutor(createExecutor());
server.start();
}

@NotNull
public static <T> Builder<T> builder(@NotNull Class<T> cls, @NotNull T object) {
return new Builder<>(object, cls);
public static <T> Builder builder(@NotNull Class<T> cls, @NotNull T object) {
Builder builder = new Builder();
builder.addController("/", cls, object);
return builder;
}

@NotNull
public static Builder builder() {
return new Builder();
}

public boolean isRunning() {
Expand Down Expand Up @@ -97,15 +170,40 @@
return server.getAddress();
}

void setLandingPage(@Nullable String landingPage) {
this.landingPage = landingPage;
}

@NotNull
protected Executor createExecutor() {
return new ThreadPoolExecutor(1, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
}

private void createEmptyRootContext(
@NotNull Gson gson,
@NotNull Predicate<InetSocketAddress> filter,
@Nullable String landingPage,
@NotNull RequestHandlerFactory handlerFactory
) {
server.createContext(
"/",
createHandler(
new ControllerDef<>("/", Object.class, EMPTY_CONTROLLER),
gson,
filter,
landingPage,
handlerFactory
)
);
}

@NotNull
private static <T> RequestHandler<T> createHandler(
@NotNull ControllerDef<T> controller,
@NotNull Gson gson,
@NotNull Predicate<InetSocketAddress> filter,
@Nullable String landingPage,
@NotNull RequestHandlerFactory handlerFactory
) {
return handlerFactory.createHandler(controller.cls, controller.instance, gson, filter, landingPage);
}

private static final Type REQUEST_TYPE = new TypeToken<Map<String, JsonElement>>() {}.getType();

public static class RequestHandler<T> implements HttpHandler {
Expand Down Expand Up @@ -155,7 +253,6 @@
try {
responseText = gson.toJson(response.object, response.type);
} catch (Throwable e) {
// Serialization error
StringWriter buf = new StringWriter();
new RpcException("JSON serialization error: " + e.getMessage(), e).printStackTrace(new PrintWriter(buf, true));

Expand Down Expand Up @@ -206,7 +303,12 @@
}

final URI uri = exchange.getRequestURI();
final String path = uri.getPath().replaceAll("^/+", "");
final String context = exchange.getHttpContext().getPath();
String path = uri.getPath();
if (path.startsWith(context)) {
path = path.substring(context.length());
}
path = path.replaceAll("^/+", "");
final Method method = mappings.get(path);

if (method == null) {
Expand Down Expand Up @@ -288,73 +390,78 @@
return new Response<>(result, type, RpcConstants.SC_OK);
}

public static final class Builder<T> {
public static final class Builder {
private static final Predicate<InetSocketAddress> DEFAULT_PREDICATE = address -> true;

private final T object;
private final Class<T> cls;
private Gson gson;
private int port;
private int backlog;
private Predicate<InetSocketAddress> filter = DEFAULT_PREDICATE;
private String landingPage;
private RequestHandlerFactory<T> handlerFactory = RequestHandler::new;
private final List<ControllerDef<?>> controllers = new ArrayList<>();
private RequestHandlerFactory handlerFactory = DEFAULT_HANDLER_FACTORY;

private Builder(@NotNull T object, @NotNull Class<T> cls) {
this.object = object;
this.cls = cls;
private Builder() {
this.gson = RpcConstants.DEFAULT_GSON;
this.port = 0;
this.backlog = 0;
}

@NotNull
public Builder<T> setGson(@NotNull Gson gson) {
public <T> Builder addController(
@NotNull String path,
@NotNull Class<T> cls,
@NotNull T instance
) {
String normalizedPath = path.startsWith("/") ? path : "/" + path;
controllers.add(new ControllerDef<>(normalizedPath, cls, instance));
return this;
}

@NotNull
public Builder setGson(@NotNull Gson gson) {
this.gson = gson;
return this;
}

@NotNull
public Builder<T> setPort(int port) {
public Builder setPort(int port) {
this.port = port;
return this;
}

@NotNull
public Builder<T> setBacklog(int backlog) {
public Builder setBacklog(int backlog) {
this.backlog = backlog;
return this;
}

@NotNull
public Builder<T> setLandingPage(String landingPage) {
public Builder setLandingPage(String landingPage) {

Check warning on line 440 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:440:39: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 440 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:440:39: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
this.landingPage = landingPage;
return this;
}

@NotNull
public Builder<T> setFilter(@NotNull Predicate<InetSocketAddress> filter) {
public Builder setFilter(@NotNull Predicate<InetSocketAddress> filter) {
this.filter = filter;
return this;
}

public Builder<T> setHandlerFactory(@NotNull RequestHandlerFactory<T> handlerFactory) {
public Builder setHandlerFactory(@NotNull RequestHandlerFactory handlerFactory) {

Check warning on line 451 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'Builder' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:451:16: warning: Reference type 'Builder' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 451 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'Builder' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:451:16: warning: Reference type 'Builder' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
this.handlerFactory = handlerFactory;
return this;
}

@NotNull
public RestServer<T> create() {
public RestServer create() {
try {
RestServer<T> restServer = new RestServer<>(cls, object, gson, filter, port, backlog, handlerFactory);
if (landingPage != null) {
restServer.setLandingPage(landingPage);
}
return restServer;
return new RestServer(controllers, gson, filter, port, backlog, landingPage, handlerFactory);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

}

private static class Response<T> {
Expand All @@ -368,4 +475,18 @@
this.code = code;
}
}

private static final class ControllerDef<T> {
final String path;
final Class<T> cls;
final T instance;

ControllerDef(String path, Class<T> cls, T instance) {

Check warning on line 484 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'T' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:484:50: warning: Reference type 'T' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 484 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'Class' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:484:36: warning: Reference type 'Class' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 484 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:484:23: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 484 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'T' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:484:50: warning: Reference type 'T' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 484 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'Class' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:484:36: warning: Reference type 'Class' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)

Check warning on line 484 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Reference type 'String' is missing a nullability annotation. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RestServer.java:484:23: warning: Reference type 'String' is missing a nullability annotation. (sh.adelessfox.checkstyle.checks.NullabilityAnnotationsCheck)
this.path = path;
this.cls = cls;
this.instance = instance;
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

public class RpcConstants {
public static final int SC_OK = 200;
public static final int SC_NO_CONTENT = 204;
public static final int SC_FORBIDDEN = 403;
public static final int SC_UNSUPPORTED = 405;
public static final int SC_NOT_FOUND = 404;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* All Rights Reserved.
* 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
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
* 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.jkiss.utils.rest;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.utils.BeanUtils;
Expand All @@ -27,9 +28,13 @@
import java.net.URI;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class RpcInvocationHandler implements InvocationHandler, RestProxy {

private static final Logger log = Logger.getLogger(RpcInvocationHandler.class.getName());

@NotNull
private final Class<?> clientClass;
protected final URI uri;
Expand Down Expand Up @@ -115,11 +120,14 @@
}

try {
// System.out.println("CONTENTS: " + contents);

Check warning on line 123 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RpcInvocationHandler.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Comment has incorrect indentation level 0, expected is 16, indentation should be the same level as line 124. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RpcInvocationHandler.java:123:1: warning: Comment has incorrect indentation level 0, expected is 16, indentation should be the same level as line 124. (com.puppycrawl.tools.checkstyle.checks.indentation.CommentsIndentationCheck)

Check warning on line 123 in modules/org.jkiss.utils/src/org/jkiss/utils/rest/RpcInvocationHandler.java

View workflow job for this annotation

GitHub Actions / Check / Lint

[checkstyle] reported by reviewdog 🐶 Comment has incorrect indentation level 0, expected is 16, indentation should be the same level as line 124. Raw Output: /github/workspace/./modules/org.jkiss.utils/src/org/jkiss/utils/rest/RpcInvocationHandler.java:123:1: warning: Comment has incorrect indentation level 0, expected is 16, indentation should be the same level as line 124. (com.puppycrawl.tools.checkstyle.checks.indentation.CommentsIndentationCheck)
return gson.fromJson(contents, returnType);
} catch (Throwable e) {
log.log(Level.WARNING, "Failed to parse json response: \n" + contents, e);
//just debug breakpoint, rethrow it
throw e;
}

} catch (RpcException e) {
if (e.getErrorClass() != null) {
Throwable error = null;
Expand Down
Loading