How to set the default JDBC URL value in H2 console using spring boot

為{幸葍}努か 提交于 2019-12-01 06:38:52

问题


I have enable the H2 console in spring boot. However, when I open the console connection page the default url is the one staved in the H2 console history. How can i configure the project to populate the URL to be the same as spring.datasource.url on project start? Currently I set the url in the console manually but I would like to have it setup automatically by the project itself.

yaml:

spring:
  h2:
    console:
      enabled: true
      path: /admin/h2


  datasource:
    url: jdbc:h2:mem:foobar

update: I know that the last connection settings are saved to ~/.h2.server.properties but what I need is to set the properties from the starting application potentially, potentially switching between several of them


回答1:


There is no hook provided to fill in settings.

The good news is that we can change that with a bit of code.

Current state

The Login screen is created in WebApp.index()

String[] settingNames = server.getSettingNames();
String setting = attributes.getProperty("setting");
if (setting == null && settingNames.length > 0) {
    setting = settingNames[0];
}
String combobox = getComboBox(settingNames, setting);
session.put("settingsList", combobox);
ConnectionInfo info = server.getSetting(setting);
if (info == null) {
    info = new ConnectionInfo();
}
session.put("setting", PageParser.escapeHtmlData(setting));
session.put("name", PageParser.escapeHtmlData(setting));
session.put("driver", PageParser.escapeHtmlData(info.driver));
session.put("url", PageParser.escapeHtmlData(info.url));
session.put("user", PageParser.escapeHtmlData(info.user));
return "index.jsp";

We want to tap into server.getSettingNames(), and precisely into server.getSettings() used underneath.

synchronized ArrayList<ConnectionInfo> getSettings() {
    ArrayList<ConnectionInfo> settings = new ArrayList<>();
    if (connInfoMap.size() == 0) {
        Properties prop = loadProperties();
        if (prop.size() == 0) {
            for (String gen : GENERIC) {
                ConnectionInfo info = new ConnectionInfo(gen);
                settings.add(info);
                updateSetting(info);
            }
        } else {
            for (int i = 0;; i++) {
                String data = prop.getProperty(Integer.toString(i));
                if (data == null) {
                    break;
                }
                ConnectionInfo info = new ConnectionInfo(data);
                settings.add(info);
                updateSetting(info);
            }
        }
    } else {
        settings.addAll(connInfoMap.values());
    }
    Collections.sort(settings);
    return settings;
}

The plan

  • disable ServletRegistrationBean<WebServlet> created by H2ConsoleAutoConfiguration
  • replace it with our config class with a subclass of WebServlet
  • our CustomH2WebServlet will override init and register CustomH2WebServer (subclass of WebServer)
  • in CustomH2WebServer we override getSettings() and we are done

The Code

@EnableConfigurationProperties({H2ConsoleProperties.class, DataSourceProperties.class})
@Configuration
public class H2Config {

    private final H2ConsoleProperties h2ConsoleProperties;

    private final DataSourceProperties dataSourceProperties;

    public H2Config(H2ConsoleProperties h2ConsoleProperties, DataSourceProperties dataSourceProperties) {
        this.h2ConsoleProperties = h2ConsoleProperties;
        this.dataSourceProperties = dataSourceProperties;
    }

    @Bean
    public ServletRegistrationBean<WebServlet> h2Console() {
        String path = this.h2ConsoleProperties.getPath();
        String urlMapping = path + (path.endsWith("/") ? "*" : "/*");
        ServletRegistrationBean<WebServlet> registration = new ServletRegistrationBean<>(
                new CustomH2WebServlet(this.dataSourceProperties.getUrl()), urlMapping);
        H2ConsoleProperties.Settings settings = this.h2ConsoleProperties.getSettings();
        if (settings.isTrace()) {
            registration.addInitParameter("trace", "");
        }
        if (settings.isWebAllowOthers()) {
            registration.addInitParameter("webAllowOthers", "");
        }
        return registration;
    }
}

package org.h2.server.web;

import javax.servlet.ServletConfig;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;

public class CustomH2WebServlet extends WebServlet {

    private final String dbUrl;

    public CustomH2WebServlet(String dbUrl) {
        this.dbUrl = dbUrl;
    }

    @Override
    public void init() {
        ServletConfig config = getServletConfig();
        Enumeration<?> en = config.getInitParameterNames();
        ArrayList<String> list = new ArrayList<>();
        while (en.hasMoreElements()) {
            String name = en.nextElement().toString();
            String value = config.getInitParameter(name);
            if (!name.startsWith("-")) {
                name = "-" + name;
            }
            list.add(name);
            if (value.length() > 0) {
                list.add(value);
            }
        }
        String[] args = list.toArray(new String[0]);
        WebServer server = new CustomH2WebServer(dbUrl);
        server.setAllowChunked(false);
        server.init(args);
        setServerWithReflection(this, server);
    }

    private static void setServerWithReflection(final WebServlet classInstance, final WebServer newValue) {
        try {
            final Field field = WebServlet.class.getDeclaredField("server");
            field.setAccessible(true);
            field.set(classInstance, newValue);
        }
        catch (SecurityException|NoSuchFieldException|IllegalArgumentException|IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }
}
package org.h2.server.web;

import java.util.ArrayList;
import java.util.Collections;

class CustomH2WebServer extends WebServer {

    private final String connectionInfo;

    CustomH2WebServer(String dbUrl) {

        this.connectionInfo = "Test H2 (Embedded)|org.h2.Driver|" +dbUrl+"|sa";
    }

    synchronized ArrayList<ConnectionInfo> getSettings() {
        ArrayList<ConnectionInfo> settings = new ArrayList<>();
        ConnectionInfo info = new ConnectionInfo(connectionInfo);
        settings.add(info);
        updateSetting(info);
        Collections.sort(settings);
        return settings;
    }
}
spring.h2.console.enabled=false
spring.datasource.url=jdbc:h2:mem:foobar

Everything went smoolthly, except of one private field that needed to be set via reflection.

The code provided works with H2 1.4.199



来源:https://stackoverflow.com/questions/56245872/how-to-set-the-default-jdbc-url-value-in-h2-console-using-spring-boot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!