diff --git a/java/maven_deps.bzl b/java/maven_deps.bzl index d59e5f8e36b20..d418e00e7cc67 100644 --- a/java/maven_deps.bzl +++ b/java/maven_deps.bzl @@ -71,6 +71,7 @@ def selenium_java_deps(): "org.eclipse.jetty:jetty-xml:%s" % jetty_version, "org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5", "org.hamcrest:hamcrest:2.2", + "org.hsqldb:hsqldb:2.5.0", "org.mockito:mockito-core:3.3.3", "org.slf4j:slf4j-jdk14:1.7.30", "org.testng:testng:7.1.0", diff --git a/java/maven_install.json b/java/maven_install.json index 43e2c8d9c9a35..e1c4860767950 100644 --- a/java/maven_install.json +++ b/java/maven_install.json @@ -1,6 +1,6 @@ { "dependency_tree": { - "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 189058187, + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 1361415682, "conflict_resolution": {}, "dependencies": [ { @@ -4215,6 +4215,42 @@ "sha256": "f49e697dbc70591f91a90dd7f741f5780f53f63f34a416d6a9879499d4d666af", "url": "https://repo1.maven.org/maven2/org/hamcrest/hamcrest/2.2/hamcrest-2.2-sources.jar" }, + { + "coord": "org.hsqldb:hsqldb:2.5.0", + "dependencies": [], + "directDependencies": [], + "exclusions": [ + "org.hamcrest:hamcrest-all", + "org.hamcrest:hamcrest-core", + "io.netty:netty-all" + ], + "file": "v1/https/repo1.maven.org/maven2/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0.jar", + "mirror_urls": [ + "https://repo1.maven.org/maven2/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0.jar", + "https://jcenter.bintray.com/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0.jar", + "https://maven.google.com/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0.jar" + ], + "sha256": "acda459cc9d6a07b39b284364e93b5f29e11877d687e9544b91778d3554d2b38", + "url": "https://repo1.maven.org/maven2/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0.jar" + }, + { + "coord": "org.hsqldb:hsqldb:jar:sources:2.5.0", + "dependencies": [], + "directDependencies": [], + "exclusions": [ + "org.hamcrest:hamcrest-all", + "org.hamcrest:hamcrest-core", + "io.netty:netty-all" + ], + "file": "v1/https/repo1.maven.org/maven2/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0-sources.jar", + "mirror_urls": [ + "https://repo1.maven.org/maven2/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0-sources.jar", + "https://jcenter.bintray.com/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0-sources.jar", + "https://maven.google.com/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0-sources.jar" + ], + "sha256": "a09e15c2ef034480b5b0998d013ca3c98c4ccfd0e1d739acdc73400fd1a21dd3", + "url": "https://repo1.maven.org/maven2/org/hsqldb/hsqldb/2.5.0/hsqldb-2.5.0-sources.jar" + }, { "coord": "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.70", "dependencies": [], diff --git a/java/server/src/org/openqa/selenium/grid/BUILD.bazel b/java/server/src/org/openqa/selenium/grid/BUILD.bazel index f61e7dac6397a..df236519778e1 100644 --- a/java/server/src/org/openqa/selenium/grid/BUILD.bazel +++ b/java/server/src/org/openqa/selenium/grid/BUILD.bazel @@ -73,6 +73,7 @@ java_export( ":base-command", "//java/server/src/org/openqa/selenium/cli", "//java/server/src/org/openqa/selenium/grid/config", + "//java/server/src/org/openqa/selenium/grid/sessionmap/jdbc", ], ) diff --git a/java/server/src/org/openqa/selenium/grid/config/ConfigException.java b/java/server/src/org/openqa/selenium/grid/config/ConfigException.java index 8926d56f374f7..cf4a0bcd818a7 100644 --- a/java/server/src/org/openqa/selenium/grid/config/ConfigException.java +++ b/java/server/src/org/openqa/selenium/grid/config/ConfigException.java @@ -22,4 +22,8 @@ public class ConfigException extends RuntimeException { public ConfigException(String message, Object... args) { super(String.format(message, args)); } + + public ConfigException(Throwable cause) { + super(cause); + } } diff --git a/java/server/src/org/openqa/selenium/grid/sessionmap/httpd/SessionMapServer.java b/java/server/src/org/openqa/selenium/grid/sessionmap/httpd/SessionMapServer.java index 733084a336a73..3bcad2a8ac656 100644 --- a/java/server/src/org/openqa/selenium/grid/sessionmap/httpd/SessionMapServer.java +++ b/java/server/src/org/openqa/selenium/grid/sessionmap/httpd/SessionMapServer.java @@ -40,6 +40,7 @@ import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static org.openqa.selenium.grid.config.StandardGridRoles.EVENT_BUS_ROLE; import static org.openqa.selenium.grid.config.StandardGridRoles.HTTPD_ROLE; +import static org.openqa.selenium.grid.config.StandardGridRoles.SESSION_MAP_ROLE; import static org.openqa.selenium.json.Json.JSON_UTF_8; import static org.openqa.selenium.remote.http.Contents.asJson; import static org.openqa.selenium.remote.http.Route.get; @@ -61,7 +62,7 @@ public String getDescription() { @Override public Set getConfigurableRoles() { - return ImmutableSet.of(EVENT_BUS_ROLE, HTTPD_ROLE); + return ImmutableSet.of(EVENT_BUS_ROLE, HTTPD_ROLE, SESSION_MAP_ROLE); } @Override diff --git a/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel new file mode 100644 index 0000000000000..2e3cd621f7f9b --- /dev/null +++ b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel @@ -0,0 +1,26 @@ +load("@rules_jvm_external//:defs.bzl", "artifact") +load("//java:version.bzl", "SE_VERSION") +load("//java:defs.bzl", "java_export") + +java_export( + name = "jdbc", + srcs = glob(["*.java"]), + maven_coordinates = "org.seleniumhq.selenium:selenium-session-map-jdbc:%s" % SE_VERSION, + pom_template = "//java/client/src/org/openqa/selenium:template-pom", + visibility = [ + "//visibility:public", + ], + deps = [ + "//java:auto-service", + "//java/client/src/org/openqa/selenium/json", + "//java/server/src/org/openqa/selenium/events", + "//java/client/src/org/openqa/selenium/remote", + "//java/server/src/org/openqa/selenium/grid/server", + "//java/server/src/org/openqa/selenium/grid/sessionmap", + "//java/server/src/org/openqa/selenium/grid/config", + "//java/server/src/org/openqa/selenium/grid/log", + "//java/server/src/org/openqa/selenium/grid/data", + artifact("com.beust:jcommander"), + artifact("com.google.guava:guava"), + ], +) diff --git a/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java new file mode 100644 index 0000000000000..a87cb84f4b2fc --- /dev/null +++ b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java @@ -0,0 +1,186 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC 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.openqa.selenium.grid.sessionmap.jdbc; + +import static org.openqa.selenium.grid.data.SessionClosedEvent.SESSION_CLOSED; + +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.ImmutableCapabilities; +import org.openqa.selenium.NoSuchSessionException; +import org.openqa.selenium.events.EventBus; +import org.openqa.selenium.grid.config.Config; +import org.openqa.selenium.grid.config.ConfigException; +import org.openqa.selenium.grid.data.Session; +import org.openqa.selenium.grid.log.LoggingOptions; +import org.openqa.selenium.grid.server.EventBusOptions; +import org.openqa.selenium.grid.sessionmap.SessionMap; +import org.openqa.selenium.internal.Require; +import org.openqa.selenium.json.Json; +import org.openqa.selenium.remote.SessionId; +import org.openqa.selenium.remote.tracing.Tracer; + +import java.io.Closeable; +import java.net.URI; +import java.net.URISyntaxException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.Logger; + + +public class JdbcBackedSessionMap extends SessionMap implements Closeable { + + private static final Json JSON = new Json(); + private static final Logger LOG = Logger.getLogger(JdbcBackedSessionMap.class.getName()); + private static final String TABLE_NAME = "sessions_map"; + private static final String SESSION_ID_COL = "session_ids"; + private static final String SESSION_CAPS_COL = "session_caps"; + private static final String SESSION_URI_COL = "session_uri"; + private final EventBus bus; + private final Connection connection; + + + public JdbcBackedSessionMap(Tracer tracer, Connection jdbcConnection, EventBus bus) { + super(tracer); + + Require.nonNull("JDBC Connection Object", jdbcConnection); + this.bus = Require.nonNull("Event bus", bus); + + this.connection = jdbcConnection; + this.bus.addListener(SESSION_CLOSED, event -> { + SessionId id = event.getData(SessionId.class); + remove(id); + }); + } + + public static SessionMap create(Config config) { + Tracer tracer = new LoggingOptions(config).getTracer(); + EventBus bus = new EventBusOptions(config).getEventBus(); + + JdbcSessionMapOptions sessionMapOptions = new JdbcSessionMapOptions(config); + + Connection connection; + + try { + connection = sessionMapOptions.getJdbcConnection(); + } catch (SQLException e) { + throw new ConfigException(e); + } + + return new JdbcBackedSessionMap(tracer, connection, bus); + } + @Override + public boolean add(Session session) { + Require.nonNull("Session to add", session); + + try { + return insertSessionStatement(session).executeUpdate() >= 1; + + } catch (SQLException e) { + throw new JdbcException(e); + } + } + + @Override + public Session get(SessionId id) throws NoSuchSessionException { + Require.nonNull("Session ID", id); + + URI uri = null; + Capabilities caps = null; + String rawUri = null; + + try (ResultSet sessions = readSessionStatement(id).executeQuery()){ + if (!sessions.next()) { + throw new NoSuchSessionException("Unable to find..."); + } + + rawUri = sessions.getString(SESSION_URI_COL); + String rawCapabilities = sessions.getString(SESSION_CAPS_COL); + + caps = rawCapabilities == null ? + new ImmutableCapabilities() : + JSON.toType(rawCapabilities, Capabilities.class); + try { + uri = new URI(rawUri); + } catch (URISyntaxException e) { + throw new NoSuchSessionException(String.format("Unable to convert session id (%s) to uri: %s", id, rawUri), e); + } + + return new Session(id, uri, caps); + } catch (SQLException e) { + throw new JdbcException(e); + } + } + + @Override + public void remove(SessionId id) { + Require.nonNull("Session ID", id); + + try { + getDeleteSqlForSession(id).executeUpdate(); + } catch (SQLException e) { + throw new JdbcException(e.getMessage()); + } + } + + @Override + public void close() { + try { + connection.close(); + } catch (SQLException e) { + LOG.warning("SQL exception while closing JDBC Connection:" + e.getMessage()); + } + } + + private PreparedStatement insertSessionStatement(Session session) throws SQLException { + PreparedStatement insertStatement = connection.prepareStatement(String.format("insert into %1$s (%2$s, %3$s, %4$s) values (?, ?, ?)", + TABLE_NAME, + SESSION_ID_COL, + SESSION_URI_COL, + SESSION_CAPS_COL)); + + insertStatement.setString(1, session.getId().toString()); + insertStatement.setString(2, session.getUri().toString()); + insertStatement.setString(3, JSON.toJson(session.getCapabilities())); + + return insertStatement; + } + + private PreparedStatement readSessionStatement(SessionId sessionId) throws SQLException { + PreparedStatement getSessionsStatement = connection.prepareStatement(String.format("select * from %1$s where %2$s = ?", + TABLE_NAME, + SESSION_ID_COL)); + + getSessionsStatement.setMaxRows(1); + getSessionsStatement.setString(1, sessionId.toString()); + + return getSessionsStatement; + } + + private PreparedStatement getDeleteSqlForSession(SessionId sessionId) throws SQLException{ + PreparedStatement deleteSessionStatement = connection.prepareStatement(String.format("delete from %1$s where %2$s = ?", + TABLE_NAME, + SESSION_ID_COL)); + + deleteSessionStatement.setString(1, sessionId.toString()); + + return deleteSessionStatement; + + } +} diff --git a/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcException.java b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcException.java new file mode 100644 index 0000000000000..1f8359cb965b5 --- /dev/null +++ b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcException.java @@ -0,0 +1,39 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC 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.openqa.selenium.grid.sessionmap.jdbc; + + +import org.openqa.selenium.WebDriverException; + +public class JdbcException extends WebDriverException { + public JdbcException() { + super(); + } + + public JdbcException(String message) { + super(message); + } + + public JdbcException(Throwable cause) { + super(cause); + } + + public JdbcException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcSessionMapFlags.java b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcSessionMapFlags.java new file mode 100644 index 0000000000000..87eade90a804c --- /dev/null +++ b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcSessionMapFlags.java @@ -0,0 +1,57 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC 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.openqa.selenium.grid.sessionmap.jdbc; + +import com.google.auto.service.AutoService; +import com.beust.jcommander.Parameter; +import org.openqa.selenium.grid.config.ConfigValue; +import org.openqa.selenium.grid.config.HasRoles; +import org.openqa.selenium.grid.config.Role; + +import java.util.Collections; +import java.util.Set; + +import static org.openqa.selenium.grid.config.StandardGridRoles.SESSION_MAP_ROLE; + + +@AutoService(HasRoles.class) +public class JdbcSessionMapFlags implements HasRoles { + + @Parameter( + names = "--jdbc-url", + description = "Database URL for making a connection.") + @ConfigValue(section = "sessions", name = "jdbc-url", example = "\"jdbc:mysql://localhost:3306/TestDatabase\"") + private String jdbcUrl; + + @Parameter( + names = "--jdbc-user", + description = "Username for the user to make a JDBC connection") + @ConfigValue(section = "sessions", name = "jdbc-user", example = "mytestUser") + private String username; + + @Parameter( + names = "--jdbc-password", + description = "Password for the user to make a JDBC connection") + @ConfigValue(section = "sessions", name = "jdbc-password", example = "hunter2") + private String password; + + @Override + public Set getRoles() { + return Collections.singleton(SESSION_MAP_ROLE); + } +} diff --git a/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcSessionMapOptions.java b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcSessionMapOptions.java new file mode 100644 index 0000000000000..92d8acf3d6ca2 --- /dev/null +++ b/java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcSessionMapOptions.java @@ -0,0 +1,48 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC 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.openqa.selenium.grid.sessionmap.jdbc; + +import org.openqa.selenium.grid.config.Config; +import org.openqa.selenium.internal.Require; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.logging.Logger; + +public class JdbcSessionMapOptions { + + private static final String SESSIONS_SECTION = "sessions"; + private static final Logger LOG = Logger.getLogger(JdbcSessionMapOptions.class.getName()); + + private final Config config; + + public JdbcSessionMapOptions(Config config) { + Require.nonNull("Config", config); + + this.config = config; + } + + public Connection getJdbcConnection() throws SQLException { + String jdbcUrl = String.valueOf(config.get(SESSIONS_SECTION, "jdbc-url")); + String jdbcUser = String.valueOf(config.get(SESSIONS_SECTION, "jdbc-user")); + String jdbcPassword = String.valueOf(config.get(SESSIONS_SECTION, "jdbc-password")); + + return DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword); + } +} diff --git a/java/server/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel b/java/server/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel new file mode 100644 index 0000000000000..1b00a852f5d10 --- /dev/null +++ b/java/server/test/org/openqa/selenium/grid/sessionmap/jdbc/BUILD.bazel @@ -0,0 +1,19 @@ +load("@rules_jvm_external//:defs.bzl", "artifact") +load("//java:defs.bzl", "java_test_suite") + +java_test_suite( + name = "MediumTests", + size = "medium", + srcs = glob(["*Test.java"]), + deps = [ + "//java/client/src/org/openqa/selenium/remote", + "//java/client/test/org/openqa/selenium/remote/tracing:tracing-support", + "//java/client/test/org/openqa/selenium/testing:test-base", + "//java/server/src/org/openqa/selenium/grid/sessionmap/jdbc", + "//java/server/src/org/openqa/selenium/events/local", + artifact("io.opentelemetry:opentelemetry-api"), + artifact("junit:junit"), + artifact("org.assertj:assertj-core"), + artifact("org.hsqldb:hsqldb"), + ], +) diff --git a/java/server/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java b/java/server/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java new file mode 100644 index 0000000000000..54355571f836a --- /dev/null +++ b/java/server/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java @@ -0,0 +1,125 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC 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.openqa.selenium.grid.sessionmap.jdbc; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openqa.selenium.ImmutableCapabilities; +import org.openqa.selenium.NoSuchSessionException; +import org.openqa.selenium.events.EventBus; +import org.openqa.selenium.events.local.GuavaEventBus; +import org.openqa.selenium.grid.data.Session; +import org.openqa.selenium.grid.sessionmap.SessionMap; +import org.openqa.selenium.remote.SessionId; +import org.openqa.selenium.remote.tracing.DefaultTestTracer; +import org.openqa.selenium.remote.tracing.Tracer; + +import java.net.URI; +import java.net.URISyntaxException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.UUID; + +public class JdbcBackedSessionMapTest { + private static Connection connection; + private static EventBus bus; + private static final Tracer tracer = DefaultTestTracer.createTracer(); + + @BeforeClass + public static void createDB() throws SQLException { + bus = new GuavaEventBus(); + connection = DriverManager.getConnection("jdbc:hsqldb:mem:testdb", "SA", ""); + Statement createStatement = connection.createStatement(); + createStatement.executeUpdate("create table sessions_map (session_ids varchar(50), session_uri varchar(30), session_caps varchar(300));"); + } + + @AfterClass + public static void killDBConnection() throws SQLException { + connection.close(); + } + + @Test(expected = NoSuchSessionException.class) + public void shouldThrowNoSuchSessionExceptionIfSessionDoesNotExists() { + SessionMap sessions = getSessionMap(); + + sessions.get(new SessionId(UUID.randomUUID())); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowIllegalArgumentExceptionIfConnectionObjectIsNull() { + SessionMap sessions = new JdbcBackedSessionMap(tracer, null, bus); + } + + @Test(expected = JdbcException.class) + public void shouldThrowNoSuchSessionExceptionIfTableDoesNotExist() throws SQLException { + Connection connection2 = DriverManager.getConnection("jdbc:hsqldb:mem:testdb2", "SA", ""); + + SessionMap sessions = new JdbcBackedSessionMap(tracer, connection2, bus); + + sessions.get(new SessionId(UUID.randomUUID())); + } + @Test + public void canCreateAJdbcBackedSessionMap() throws URISyntaxException { + SessionMap sessions = getSessionMap(); + + Session expected = new Session( + new SessionId(UUID.randomUUID()), + new URI("http://example.com/foo"), + new ImmutableCapabilities("key", "value")); + sessions.add(expected); + + SessionMap reader = getSessionMap(); + + Session seen = reader.get(expected.getId()); + + assertThat(seen).isEqualTo(expected); + } + + @Test + public void shouldBeAbleToRemoveSessions() throws URISyntaxException { + SessionMap sessions = getSessionMap(); + + Session expected = new Session( + new SessionId(UUID.randomUUID()), + new URI("http://example.com/foo"), + new ImmutableCapabilities("key", "value")); + sessions.add(expected); + + SessionMap reader = getSessionMap(); + + reader.remove(expected.getId()); + + try { + reader.get(expected.getId()); + fail("Oh noes!"); + } catch (NoSuchSessionException ignored) { + // This is expected + } + } + + private JdbcBackedSessionMap getSessionMap() { + return new JdbcBackedSessionMap(tracer, connection, bus); + } + +}