-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Jdbc session map #8378
WIP: Jdbc session map #8378
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Welcome aboard :) This is a great start. I've added some comments, but we're also chatting on Slack, so you can ask things there too.
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package org.openqa.selenium.grid.sessionmap.config; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this to the jdbc
package you've just created.
@@ -40,6 +43,18 @@ public SessionMapOptions(Config config) { | |||
this.config = config; | |||
} | |||
|
|||
public Connection getJdbcConnection() throws SQLException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is only used in the JdbcSessionMap
, it would be better to either move this into a helper method in the JdbcSessionMap
, or create a jdbc.JdbcOptions
class.
public class JdbcBackedSessionMap extends SessionMap implements Closeable { | ||
|
||
private static final Json JSON = new Json(); | ||
private String tableName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
java/server/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java
Show resolved
Hide resolved
try { | ||
connection = sessionMapOptions.getJdbcConnection(); | ||
} catch (SQLException e) { | ||
// TODO: Handle SQLException |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can throw a ConfigException
here.
Statement insertStatement = connection.createStatement(); | ||
// TODO: Insert Call | ||
} catch (SQLException e) { | ||
// TODO: Handle SQLException |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can create a custom JdbcException
which extends RuntimeException
or WebDriverException
if you want to.
try { | ||
connection.close(); | ||
} catch (SQLException e) { | ||
e.printStackTrace(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log using a java.util.logging.Logger
at the warning level and swallow. There's nothing sensible to be done on the way out.
30f1300
to
00854be
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great start. It's shaping up well.
Capabilities caps = null; | ||
|
||
try { | ||
ResultSet sessions = readSessionStatement(id).executeQuery(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use try-with-resources so that the ResultSet
will be closed properly.
try { | ||
ResultSet sessions = readSessionStatement(id).executeQuery(); | ||
|
||
while(sessions.next()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How many results are you expecting? Perhaps use a LIMIT 1
to ensure only one row at most is sent back?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the limit in place, you either have 0 or 1 columns. You should be able to replace the while
with an if
, which may simplify this a little, as you can fast-path out by throwing the NoSuchSessionException
if there's no session found.
try { | ||
uri = new URI(rawUri); | ||
} | ||
catch (URISyntaxException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Java style is to put the closing brace and the catch
statement on the same line } catch (URISyntaxException e) {
|
||
sessions.close(); | ||
|
||
if (uri == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those can't happen. If rawUri
was null, then new URI
would have thrown.
} | ||
|
||
private PreparedStatement insertSessionStatement(Session session) throws SQLException { | ||
PreparedStatement insertStatement = connection.prepareStatement("insert into " + tableName + " values(?,?)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you know that tableName
is not, itself, an SQL injection attack?
private String sessionUri(SessionId sessionId) { | ||
Require.nonNull("Session ID", sessionId); | ||
|
||
return "session:" + sessionId.toString() + ":uri"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The session id should be a UUID (it will be unique, even if it's not formatted correctly)
|
||
private PreparedStatement getDeleteSqlForSession(SessionId sessionId) throws SQLException{ | ||
PreparedStatement deleteSessionStatement = connection.prepareStatement("delete from " + tableName + " where " + sessionIdCol + " like ?"); | ||
deleteSessionStatement.setString(1, sessionUri(sessionId).concat("%")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A session id is unique and complete. You don't need to wildcard it.
@Parameter( | ||
names = "--jdbc-sessionid-column", | ||
description = "Column name where session id will be stored") | ||
@ConfigValue(section = "sessions", name = "jdbc-sessionid-column", example = "session_id") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make life easy for yourself. Don't let this be configurable.
@Parameter( | ||
names = "--jdbc-table", | ||
description = "Name of the table in database to store sessions in.") | ||
@ConfigValue(section = "sessions", name = "jdbc-table", example = "myP@ssw%d") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be very tempted to not make this configurable. If someone wants to use this, they'll just need to create the table needed.
names = "--jdbc-capabilities-column", | ||
description = "Column name where session id will be stored") | ||
@ConfigValue(section = "sessions", name = "jdbc-capabilities-column", example = "capabilities") | ||
private String sessionCapsColumn; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, don't let this be configurable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple of nits, and we're there. This looks great :) Thank you!
try { | ||
ResultSet sessions = readSessionStatement(id).executeQuery(); | ||
|
||
while(sessions.next()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the limit in place, you either have 0 or 1 columns. You should be able to replace the while
with an if
, which may simplify this a little, as you can fast-path out by throwing the NoSuchSessionException
if there's no session found.
Tracer tracer = new LoggingOptions(config).getTracer(); | ||
JdbcSessionMapOptions sessionMapOptions = new JdbcSessionMapOptions(config); | ||
String tableName = sessionMapOptions.getJdbcTableName(); | ||
String sessionIdColName = sessionMapOptions.getJdbcSessionIdColName(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these should be hard-coded now, so there's no need to look them up in the options :)
private static final Logger LOG = Logger.getLogger(JdbcBackedSessionMap.class.getName()); | ||
private final Connection connection; | ||
private final String tableName; | ||
private final String sessionIdCol; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're hard-coding the table names, make these constants or just use magic strings....
|
||
private static final String SESSIONS_SECTION = "sessions"; | ||
private static final Logger LOG = Logger.getLogger(JdbcSessionMapOptions.class.getName()); | ||
private static final String tableName = "sessions_map"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's safe to move these constants into the JdbcSessionMap
itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more round, then let's land this and iterate :)
@@ -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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this deliberate? Or a debugging aid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After speaking on Slack, it seems that this is here to help bootstrap things so that new flags appear on the classpath.
|
||
private static final Json JSON = new Json(); | ||
private static final Logger LOG = Logger.getLogger(JdbcBackedSessionMap.class.getName()); | ||
private final EventBus bus; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: place the instance fields below the constants.
try { | ||
connection = sessionMapOptions.getJdbcConnection(); | ||
} catch (SQLException e) { | ||
throw new ConfigException(e.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add the underlying exception too? new ConfigException(e)
String rawUri = null; | ||
|
||
try (ResultSet sessions = readSessionStatement(id).executeQuery()){ | ||
if (sessions.next()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer multiple return points and less indenting:
if (!session.next()) {
throw new NoSuchSessionException("Unable to find...");
}
} | ||
|
||
private PreparedStatement insertSessionStatement(Session session) throws SQLException { | ||
PreparedStatement insertStatement = connection.prepareStatement("insert into " + TABLE_NAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Newline before the arguments begin. That avoids excessive line length and makes the code a bit easier to read.
} | ||
|
||
private PreparedStatement insertSessionStatement(Session session) throws SQLException { | ||
PreparedStatement insertStatement = connection.prepareStatement("insert into " + TABLE_NAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using String.format
instead of concatenating strings. When there's just one, it's fine, but it starts to get hard to read after a while.
} | ||
|
||
private PreparedStatement readSessionStatement(SessionId sessionId) throws SQLException { | ||
PreparedStatement getSessionsStatement = connection.prepareStatement("select * from " + TABLE_NAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: prefer String.format
for readability
@Parameter( | ||
names = "--jdbc-password", | ||
description = "Password for the user to make a JDBC connection") | ||
@ConfigValue(section = "sessions", name = "jdbc-password", example = "myP@ssw%d") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Humorous aside: We tend to use hunter2
as the password of choice: https://knowyourmeme.com/memes/hunter2 https://w3c.github.io/webdriver/#example-7
private final Config config; | ||
|
||
public JdbcSessionMapOptions(Config config) { | ||
this.config = config; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reuire.nonNull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Congratulations on landing this. It's a lovely feature!
@@ -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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After speaking on Slack, it seems that this is here to help bootstrap things so that new flags appear on the classpath.
Provides an implementation of the `SessionMap` that's backed by JDBC.
Thanks for contributing to Selenium!
A PR well described will help maintainers to quickly review and merge it
Before submitting your PR, please check our contributing guidelines.
Avoid large PRs, help reviewers by making them as simple and short as possible.
Description
Fixes #8172
Motivation and Context
Types of changes
Checklist