diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..98898ff
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/.metadata/
+copy-go-contrib.bat
+/nkjmlab-go-webapp/pom.xml.versionsBackup
diff --git a/go-build.bat b/go-build.bat
new file mode 100644
index 0000000..0483be8
--- /dev/null
+++ b/go-build.bat
@@ -0,0 +1,4 @@
+@echo off
+cd /d %~dp0
+jps -lm|grep org.nkjmlab.go. | gawk "{print $1}" | xargs -r -n1 taskkill /F /T /PID
+call mvn clean install dependency:copy-dependencies -DoutputDirectory=target/lib -f nkjmlab-go-webapp
diff --git a/go-fullbuild.bat b/go-fullbuild.bat
new file mode 100644
index 0000000..1fddeb5
--- /dev/null
+++ b/go-fullbuild.bat
@@ -0,0 +1,6 @@
+@echo off
+cd /d %~dp0
+jps -lm|grep org.nkjmlab.go. | gawk "{print $1}" | xargs -r -n1 taskkill /F /T /PID
+call mvn clean install -DskipTests=true -f sorm4j
+call mvn clean install -DskipTests=true -f nkjmlab-utils
+call mvn clean install dependency:copy-dependencies -DoutputDirectory=target/lib -f nkjmlab-go-webapp
diff --git a/nkjmlab-go-webapp/pom.xml b/nkjmlab-go-webapp/pom.xml
index e12bcfa..fc6de6c 100644
--- a/nkjmlab-go-webapp/pom.xml
+++ b/nkjmlab-go-webapp/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.nkjmlab
nkjmlab-go-webapp
- 0.9.5
+ 0.10.0
Go Web Application
https://github.com/nkjmlab/nkjmlab-go-webapp
@@ -24,49 +24,56 @@
https://github.com/yuu-nkjm/nkjmlab-go-webapp
HEAD
-
- 0.8.2
- 3.0.15.RELEASE
- 1.4.8
+ 0.9.4
+ 1.4.16
UTF-8
true
true
-
org.nkjmlab
- nkjmlab-utils-core
+ nkjmlab-utils-helper
${nkjmlab-utils-version}
-
org.nkjmlab
sorm4j
${sorm4j-version}
-
+
- javax.servlet
- javax.servlet-api
- 4.0.1
+ jakarta.servlet
+ jakarta.servlet-api
+ 6.0.0
+ provided
-
io.javalin
javalin
- 4.6.3
+ 5.5.0
+
+
+
+ io.javalin
+ javalin-rendering
+ 5.5.0
+
+
+
+ org.eclipse.jetty.websocket
+ websocket-jetty-client
+ 11.0.15
-
com.google.firebase
firebase-admin
- 9.0.0
+ 9.1.1
@@ -74,32 +81,23 @@
h2
2.1.214
-
com.zaxxer
HikariCP
5.0.1
-
org.thymeleaf
thymeleaf
- ${thymeleaf-version}
+ 3.1.1.RELEASE
-
-
- org.thymeleaf.extras
- thymeleaf-extras-java8time
- 3.0.4.RELEASE
-
-
commons-io
commons-io
- 2.11.0
+ 2.12.0
@@ -107,50 +105,186 @@
commons-lang3
3.12.0
-
-
- com.orangesignal
- orangesignal-csv
- 2.2.1
-
-
com.fasterxml.jackson.core
jackson-databind
- 2.13.3
+ 2.15.2
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
- 2.13.3
+ 2.15.2
-
-
+
org.apache.logging.log4j
- log4j-slf4j-impl
- 2.17.2
+ log4j-slf4j2-impl
+ 2.20.0
org.apache.logging.log4j
log4j-core
- 2.17.2
+ 2.20.0
+
+
+ org.webjars.npm
+ jquery
+ 3.7.0
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ bootstrap
+ 5.3.0
+
+
+ *
+ *
+
+
+
+
+ org.webjars.bower
+ bootstrap-treeview
+ 1.2.0
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ sweetalert2
+ 11.7.5
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ fortawesome__fontawesome-free
+ 6.4.0
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ stacktrace-js
+ 2.0.2
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ clipboard
+ 2.0.11
+
+
+ *
+ *
+
+
+
+
+ org.webjars
+ datatables
+ 1.13.2
+
+
+ *
+ *
+
+
+
+
+
+ org.webjars.npm
+ firebase
+ 9.19.1
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ firebaseui
+ 6.0.2
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ emojionearea
+ 3.4.2
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ blueimp-load-image
+ 5.16.0
+
+
+ *
+ *
+
+
+
+
+ org.webjars.npm
+ ua-parser-js
+ 1.0.35
+
+
+ *
+ *
+
+
org.junit.jupiter
junit-jupiter-engine
- 5.9.0-M1
+ 5.9.3
test
org.assertj
assertj-core
- 3.23.1
+ 3.24.2
test
@@ -172,6 +306,21 @@
compile
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ 2.15.0
+
+
+
+
+ regex
+ (?i).*(alpha|beta|snapshot|pre|rc|M\d).*
+
+
+
+
+
\ No newline at end of file
diff --git a/nkjmlab-go-webapp/src/assembly/jar-with-dependencies.xml b/nkjmlab-go-webapp/src/assembly/jar-with-dependencies.xml
deleted file mode 100644
index 684315c..0000000
--- a/nkjmlab-go-webapp/src/assembly/jar-with-dependencies.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
- jar-with-dependencies
-
- dir
-
- false
-
-
- /
- true
- true
- runtime
-
-
-
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java
new file mode 100644
index 0000000..b0e9dd8
--- /dev/null
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java
@@ -0,0 +1,100 @@
+package org.nkjmlab.go.javalin;
+
+import javax.sql.DataSource;
+import org.h2.jdbcx.JdbcConnectionPool;
+import org.nkjmlab.sorm4j.util.h2.datasource.H2LocalDataSourceFactory;
+import org.nkjmlab.util.jackson.JacksonMapper;
+import org.nkjmlab.util.java.concurrent.ForkJoinPoolUtils;
+import org.nkjmlab.util.java.json.FileDatabaseConfigJson;
+import org.nkjmlab.util.java.lang.ResourceUtils;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class DataSourceManager {
+
+ private static final org.apache.logging.log4j.Logger log =
+ org.apache.logging.log4j.LogManager.getLogger();
+
+ private static final int DEFAULT_MAX_CONNECTIONS =
+ Math.min(ForkJoinPoolUtils.availableProcessors() * 2 * 2, 10);
+
+ private static final int DEFAULT_TIMEOUT_SECONDS = 30;
+
+ private H2LocalDataSourceFactory factory;
+
+ public DataSourceManager() {
+ FileDatabaseConfigJson fileDbConf = getFileDbConfig();
+ H2LocalDataSourceFactory factory =
+ H2LocalDataSourceFactory.builder(fileDbConf.databaseDirectory, fileDbConf.databaseName,
+ fileDbConf.username, fileDbConf.password).build();
+ this.factory = factory;
+ factory.makeFileDatabaseIfNotExists();
+ log.info("server jdbcUrl={}", factory.getServerModeJdbcUrl());
+ }
+
+ public DataSource createHikariInMemoryDataSource() {
+ return createHikariDataSource(factory.getInMemoryModeJdbcUrl(), factory.getUsername(),
+ factory.getPassword());
+ }
+
+ public DataSource createHikariServerModeDataSource() {
+ return createHikariDataSource(factory.getServerModeJdbcUrl(), factory.getUsername(),
+ factory.getPassword());
+ }
+
+ public JdbcConnectionPool createH2InMemoryDataSource() {
+ return createH2DataSource(factory.getInMemoryModeJdbcUrl(), factory.getUsername(),
+ factory.getPassword());
+ }
+
+ public JdbcConnectionPool createH2ServerModeDataSource() {
+ return createH2DataSource(factory.getServerModeJdbcUrl(), factory.getUsername(),
+ factory.getPassword());
+ }
+
+
+ private static HikariDataSource createHikariDataSource(String url, String user, String password) {
+ HikariConfig config = new HikariConfig();
+ config.setJdbcUrl(url);
+ config.setUsername(user);
+ config.setPassword(password);
+ config.setMaximumPoolSize(DEFAULT_MAX_CONNECTIONS);
+ config.setConnectionTimeout(DEFAULT_TIMEOUT_SECONDS * 1000);
+ config.addDataSourceProperty("useServerPrepStmts", "true");
+ config.addDataSourceProperty("cachePrepStmts", "true");
+ config.addDataSourceProperty("prepStmtCacheSize", "250");
+ config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
+ config.addDataSourceProperty("minimumIdle", "2048");
+ return new HikariDataSource(config);
+ }
+
+
+ private static JdbcConnectionPool createH2DataSource(String url, String user, String password) {
+ JdbcConnectionPool ds = JdbcConnectionPool.create(url, user, password);
+ ds.setMaxConnections(DEFAULT_MAX_CONNECTIONS);
+ ds.setLoginTimeout(DEFAULT_TIMEOUT_SECONDS);
+ return ds;
+ }
+
+ private static FileDatabaseConfigJson getFileDbConfig() {
+ try {
+ return JacksonMapper.getDefaultMapper()
+ .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json"),
+ FileDatabaseConfigJson.Builder.class)
+ .build();
+ } catch (Exception e) {
+ log.warn("Try to load h2.json.default");
+ return JacksonMapper.getDefaultMapper()
+ .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json.default"),
+ FileDatabaseConfigJson.Builder.class)
+ .build();
+ }
+ }
+
+ public H2LocalDataSourceFactory getFactory() {
+ return factory;
+ }
+
+
+
+}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java
new file mode 100644
index 0000000..98d9b07
--- /dev/null
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java
@@ -0,0 +1,68 @@
+package org.nkjmlab.go.javalin;
+
+import java.util.Set;
+import org.nkjmlab.go.javalin.auth.GoAuthService;
+import org.nkjmlab.go.javalin.model.relation.UsersTable;
+import org.nkjmlab.go.javalin.model.relation.UsersTable.User;
+import io.javalin.http.Context;
+import io.javalin.http.Handler;
+import io.javalin.security.AccessManager;
+import io.javalin.security.RouteRole;
+
+public class GoAccessManager implements AccessManager {
+
+ public enum UserRole implements RouteRole {
+
+ BEFORE_LOGIN, GUEST, STUDENT, ADMIN;
+
+ static final UserRole[] LOGIN_ROLES = new UserRole[] {GUEST, STUDENT, ADMIN};
+
+ }
+
+ private final UsersTable usersTable;
+ private final GoAuthService authService;
+
+ public GoAccessManager(UsersTable usersTable, GoAuthService authService) {
+ this.usersTable = usersTable;
+ this.authService = authService;
+ }
+
+ UserRole toUserRole(UsersTable usersTable, String sessionId) {
+ if (!authService.isSignin(sessionId)) {
+ return UserRole.BEFORE_LOGIN;
+ }
+
+ User u = authService.toSigninSession(sessionId)
+ .map(login -> usersTable.selectByPrimaryKey(login.userId())).orElse(null);
+ if (u == null) {
+ return UserRole.BEFORE_LOGIN;
+ }
+ if (u.isAdmin()) {
+ return UserRole.ADMIN;
+ }
+ if (u.isStudent()) {
+ return UserRole.STUDENT;
+ }
+ if (u.isGuest()) {
+ return UserRole.GUEST;
+ }
+ return UserRole.BEFORE_LOGIN;
+ }
+
+
+ @Override
+ public void manage(Handler handler, Context ctx, Set extends RouteRole> routeRoles)
+ throws Exception {
+ if (routeRoles.size() == 0) {
+ handler.handle(ctx);
+ } else if (routeRoles.contains(toUserRole(usersTable, ctx.req().getSession().getId()))) {
+ handler.handle(ctx);
+ } else {
+ ctx.redirect("/app/index.html");
+ }
+
+
+ }
+
+
+}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAppHandlers.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAppHandlers.java
new file mode 100644
index 0000000..0abff3e
--- /dev/null
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAppHandlers.java
@@ -0,0 +1,250 @@
+package org.nkjmlab.go.javalin;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.nkjmlab.go.javalin.GoAccessManager.UserRole;
+import org.nkjmlab.go.javalin.auth.GoAuthService;
+import org.nkjmlab.go.javalin.auth.GoAuthService.SigninSession;
+import org.nkjmlab.go.javalin.model.relation.GameRecordsTable.GameRecord;
+import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState;
+import org.nkjmlab.go.javalin.model.relation.HandUpsTable.HandUp;
+import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login;
+import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable.MatchingRequest;
+import org.nkjmlab.go.javalin.model.relation.UsersTable;
+import org.nkjmlab.go.javalin.model.relation.UsersTable.User;
+import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager;
+import org.nkjmlab.sorm4j.common.Tuple.Tuple2;
+import org.nkjmlab.util.java.net.UrlUtils;
+import org.nkjmlab.util.java.web.ViewModel;
+import io.javalin.Javalin;
+import io.javalin.http.Context;
+import io.javalin.http.Handler;
+import jakarta.servlet.http.HttpServletRequest;
+
+public class GoAppHandlers {
+
+ public static class GoHandler implements Handler {
+
+ private final Function>>>>> handler;
+ private final GoTables goTables;
+ private final GoAuthService authService;
+
+ public GoHandler(GoTables goTables, GoAuthService authService,
+ Function>>>>> handler) {
+ this.handler = handler;
+ this.goTables = goTables;
+ this.authService = authService;
+ }
+
+ @Override
+ public void handle(Context ctx) throws Exception {
+ String filePath = UrlUtils.of(ctx.url()).getPath().replaceFirst("^/app/", "");
+ ViewModel.Builder model = createDefaultViewModelBuilder(goTables.usersTable, ctx.req());
+ Optional session = authService.toSigninSession(ctx.req().getSession().getId());
+
+ handler.apply(goTables).apply(ctx).apply(filePath).apply(model).accept(session);
+ }
+
+ private ViewModel.Builder createDefaultViewModelBuilder(UsersTable usersTable,
+ HttpServletRequest request) {
+ Map map = ViewModel.builder()
+ .setFileModifiedDate(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), 10, "js", "css")
+ .put("webjars", GoWebAppConfig.WEB_APP_CONFIG.getWebJars())
+ .put("currentUser", getCurrentUserAccount(usersTable, request)).build();
+ return ViewModel.builder(map);
+ }
+
+ private User getCurrentUserAccount(UsersTable usersTable, HttpServletRequest request) {
+ Optional u = authService.toSigninSession(request.getSession().getId())
+ .map(uid -> usersTable.selectByPrimaryKey(uid.userId()));
+ return u.orElse(new User());
+ }
+ }
+
+ private static class PlayGoHandler extends GoHandler {
+
+ public PlayGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ session.ifPresent(opts -> {
+ boolean attend = gtbl.loginsTable.isAttendance(opts.userId());
+ model.put("isAttendance", attend);
+ model.put("problemGroupsJson", gtbl.problemsTable.getProblemGroupsNode());
+ });
+ ctx.render(filePath, model.build());
+ });
+
+ }
+ }
+
+ private static class AppIndexGoHandler extends GoHandler {
+ public AppIndexGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ session.ifPresent(opts -> {
+ boolean attend = gtbl.loginsTable.isAttendance(opts.userId());
+ model.put("isAttendance", attend);
+ model.put("problemGroupsJson", gtbl.problemsTable.getProblemGroupsNode());
+ });
+ ctx.render(filePath, model.build());
+ });
+ }
+ }
+
+ private static class PlayersAllGoHandler extends GoHandler {
+ public PlayersAllGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ List> users = gtbl.usersTable.readAllWithLastLogin();
+ List loginJsons =
+ users.stream().map(t -> new GoAppHandlers.LoginJson(t.getT2(), t.getT1()))
+ .collect(Collectors.toList());
+ model.put("userAccounts", loginJsons);
+ ctx.render("players.html", model.build());
+ });
+ }
+ }
+ private static class PlayersGoHandler extends GoHandler {
+ public PlayersGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ List> users = gtbl.usersTable.readAllWithLastLogin();
+ List loginJsons = users.stream().filter(t -> t.getT1().isStudent())
+ .map(t -> new GoAppHandlers.LoginJson(t.getT2(), t.getT1()))
+ .collect(Collectors.toList());
+ model.put("userAccounts", loginJsons);
+ ctx.render(filePath, model.build());
+ });
+ }
+ }
+ private static class GamesAllGoHandler extends GoHandler {
+ public GamesAllGoHandler(GoTables goTables, GoAuthService authService,
+ WebsocketSessionsManager websocketManager) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ List tmp =
+ gtbl.gameStatesTables.readTodayGameJsons().stream().map(gsj -> {
+ String gameId = gsj.gameId();
+ GoAppHandlers.GameStateViewJson json = new GoAppHandlers.GameStateViewJson(gsj,
+ gtbl.handsUpTable.selectByPrimaryKey(gameId),
+ websocketManager.getWatchingUniqueStudentsNum(gameId));
+ return json;
+ }).collect(Collectors.toList());
+ model.put("games", tmp);
+ ctx.render("games.html", model.build());
+ });
+ }
+ }
+ private static class GamesGoHandler extends GoHandler {
+ public GamesGoHandler(GoTables goTables, GoAuthService authService,
+ WebsocketSessionsManager websocketManager) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ List gids = websocketManager.readActiveGameIdsOrderByGameId();
+ List tmp =
+ gids.stream().map(gid -> gtbl.gameStatesTables.readLatestGameState(gid))
+ .map(gsj -> new GoAppHandlers.GameStateViewJson(gsj,
+ gtbl.handsUpTable.selectByPrimaryKey(gsj.gameId()),
+ websocketManager.getWatchingUniqueStudentsNum(gsj.gameId())))
+ .collect(Collectors.toList());
+ model.put("games",
+ tmp.stream().filter(j -> j.watchingStudentsNum() > 0).collect(Collectors.toList()));
+ ctx.render(filePath, model.build());
+ });
+ }
+ }
+ private static class GameRecordTableGoHandler extends GoHandler {
+ public GameRecordTableGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ String userId = ctx.queryParam("userId");
+ List records = gtbl.gameRecordsTable.readByUserId(userId);
+ model.put("records", records);
+ ctx.render("fragment/game-record-table.html", model.build());
+ });
+ }
+ }
+
+ private static class QuestionTableGoHandler extends GoHandler {
+ public QuestionTableGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ List gids = gtbl.handsUpTable.readAllGameIds();
+ List tmp = gtbl.gameStatesTables.readLatestBoardsJson(gids)
+ .stream().map(gsj -> new GoAppHandlers.GameStateViewJson(gsj,
+ gtbl.handsUpTable.selectByPrimaryKey(gsj.gameId()), 0))
+ .toList();
+ model.put("games", tmp);
+ ctx.render(filePath, model.build());
+ });
+ }
+ }
+
+ private static class WaitingRequestGoHandler extends GoHandler {
+ public WaitingRequestGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ String userId = ctx.queryParam("userId");
+ if (userId != null) {
+ List tmp = gtbl.matchingRequestsTable.readRequests();
+ model.put("requests",
+ tmp.stream().filter(r -> r.userId().equals(userId)).collect(Collectors.toList()));
+ } else {
+ model.put("requests", gtbl.matchingRequestsTable.readRequests());
+ }
+ ctx.render(filePath, model.build());
+ });
+ }
+ }
+ private static class WaitingRequestSmallGoHandler extends GoHandler {
+ public WaitingRequestSmallGoHandler(GoTables goTables, GoAuthService authService) {
+ super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ String userId = ctx.queryParam("userId");
+ List tmp = gtbl.matchingRequestsTable.readRequests();
+ MatchingRequest req = tmp.stream().filter(r -> r.userId().equals(userId)).findAny()
+ .orElse(new MatchingRequest());
+ model.put("req", req);
+ model.put("reqNum", tmp.size());
+ ctx.render(filePath, model.build());
+ });
+ }
+ }
+
+
+ public static void prepareGetHandler(Javalin app, WebsocketSessionsManager websocketManager,
+ GoTables goTables, GoAuthService authService) {
+
+ app.get("/app", ctx -> ctx.redirect("/app/index.html"));
+ app.get("/app/index.html", new AppIndexGoHandler(goTables, authService));
+ app.get("/app/play.html", new PlayGoHandler(goTables, authService), UserRole.LOGIN_ROLES);
+ app.get("/app/players-all.html", new PlayersAllGoHandler(goTables, authService),
+ UserRole.ADMIN);
+ app.get("/app/players.html", new PlayersGoHandler(goTables, authService), UserRole.ADMIN);
+ app.get("/app/games-all.html", new GamesAllGoHandler(goTables, authService, websocketManager),
+ UserRole.ADMIN);
+ app.get("/app/games.html", new GamesGoHandler(goTables, authService, websocketManager),
+ UserRole.ADMIN);
+ app.get("/app/fragment/game-record-table.html",
+ new GameRecordTableGoHandler(goTables, authService), UserRole.LOGIN_ROLES);
+ app.get("/app/fragment/question-table*", new QuestionTableGoHandler(goTables, authService),
+ UserRole.ADMIN);
+
+ app.get("/app/fragment/waiting-request-table.html",
+ new WaitingRequestGoHandler(goTables, authService), UserRole.ADMIN);
+
+ app.get("/app/fragment/waiting-request-table-small.html",
+ new WaitingRequestSmallGoHandler(goTables, authService), UserRole.LOGIN_ROLES);
+
+ app.get("/app/*",
+ new GoHandler(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> {
+ ctx.render(filePath, model.build());
+ }));
+
+ }
+
+ public static record LoginJson(Login login, User user) {
+
+ }
+
+ public static record GameStateViewJson(GameState gameState, HandUp handUp,
+ int watchingStudentsNum) {
+
+ }
+
+
+}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java
index 4d67d91..2e963ad 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java
@@ -1,120 +1,43 @@
package org.nkjmlab.go.javalin;
-import java.io.File;
-import java.nio.file.Files;
-import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import javax.servlet.http.HttpServletRequest;
-import javax.sql.DataSource;
-import org.h2.jdbcx.JdbcConnectionPool;
-import org.nkjmlab.go.javalin.fbauth.AuthService;
-import org.nkjmlab.go.javalin.fbauth.AuthServiceInterface;
-import org.nkjmlab.go.javalin.fbauth.FirebaseUserSession;
+import org.nkjmlab.go.javalin.auth.AuthService;
+import org.nkjmlab.go.javalin.auth.GoAuthService;
import org.nkjmlab.go.javalin.jsonrpc.GoJsonRpcService;
-import org.nkjmlab.go.javalin.model.relation.GameRecordsTable;
-import org.nkjmlab.go.javalin.model.relation.GameRecordsTable.GameRecord;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTable;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameStateJson;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTables;
-import org.nkjmlab.go.javalin.model.relation.HandUpsTable;
-import org.nkjmlab.go.javalin.model.relation.HandUpsTable.HandUp;
-import org.nkjmlab.go.javalin.model.relation.LoginsTable;
-import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login;
-import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable;
-import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable.MatchingRequest;
-import org.nkjmlab.go.javalin.model.relation.PasswordsTable;
-import org.nkjmlab.go.javalin.model.relation.ProblemsTable;
-import org.nkjmlab.go.javalin.model.relation.UsersTable;
-import org.nkjmlab.go.javalin.model.relation.UsersTable.User;
-import org.nkjmlab.go.javalin.model.relation.VotesTable;
import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager;
-import org.nkjmlab.go.javalin.websocket.WebsoketSessionsTable;
-import org.nkjmlab.sorm4j.common.Tuple.Tuple2;
-import org.nkjmlab.sorm4j.internal.util.ParameterizedStringUtils;
-import org.nkjmlab.sorm4j.internal.util.Try;
-import org.nkjmlab.util.h2.H2LocalDataSourceFactory;
-import org.nkjmlab.util.h2.H2ServerUtils;
+import org.nkjmlab.sorm4j.util.h2.server.H2TcpServerProcess;
+import org.nkjmlab.sorm4j.util.h2.server.H2TcpServerProperties;
+import org.nkjmlab.util.firebase.auth.BasicFirebaseAuthHandler;
+import org.nkjmlab.util.firebase.auth.FirebaseAuthHandler;
import org.nkjmlab.util.jackson.JacksonMapper;
-import org.nkjmlab.util.java.concurrent.ForkJoinPoolUtils;
-import org.nkjmlab.util.java.io.SystemFileUtils;
-import org.nkjmlab.util.java.json.FileDatabaseConfigJson;
+import org.nkjmlab.util.java.function.Try;
import org.nkjmlab.util.java.lang.ProcessUtils;
import org.nkjmlab.util.java.lang.ResourceUtils;
import org.nkjmlab.util.java.lang.SystemPropertyUtils;
-import org.nkjmlab.util.javax.servlet.JsonRpcService;
-import org.nkjmlab.util.javax.servlet.UserSession;
-import org.nkjmlab.util.javax.servlet.ViewModel;
-import org.nkjmlab.util.javax.servlet.ViewModel.Builder;
-import org.nkjmlab.util.jsonrpc.JsonRpcRequest;
-import org.nkjmlab.util.jsonrpc.JsonRpcResponse;
-import org.nkjmlab.util.thymeleaf.TemplateEngineBuilder;
+import org.nkjmlab.util.javalin.JavalinJsonRpcService;
+import org.nkjmlab.util.thymeleaf.ThymeleafTemplateEnginBuilder;
import org.thymeleaf.TemplateEngine;
-import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
-import com.zaxxer.hikari.HikariConfig;
-import com.zaxxer.hikari.HikariDataSource;
import io.javalin.Javalin;
import io.javalin.http.staticfiles.Location;
-import io.javalin.plugin.rendering.template.JavalinThymeleaf;
+import io.javalin.rendering.template.JavalinThymeleaf;
public class GoApplication {
private static final org.apache.logging.log4j.Logger log =
org.apache.logging.log4j.LogManager.getLogger();
- private static final File APP_ROOT_DIR = ResourceUtils.getResourceAsFile("/");
- private static final String WEBROOT_DIR_NAME = "/webroot";
- private static final File WEBROOT_DIR = new File(APP_ROOT_DIR, WEBROOT_DIR_NAME);
- private static final File USER_HOME_DIR = SystemFileUtils.getUserHomeDirectory();
- public static final File BACKUP_DIR = new File(USER_HOME_DIR, "go-bkup/");
- public static final File PROBLEM_DIR = new File(APP_ROOT_DIR, "problem");
- public static final File PROBLEM_BACKUP_DIR = new File(APP_ROOT_DIR, "problem-auto-bkup");
-
- public static final File CURRENT_ICON_DIR = new File(WEBROOT_DIR, "img/icon");
- public static final File UPLOADED_ICON_DIR = new File(WEBROOT_DIR, "img/icon-uploaded");
- public static final File RANDOM_ICON_DIR = new File(WEBROOT_DIR, "img/icon-random");
- public static final File INITIAL_ICON_DIR = new File(WEBROOT_DIR, "img/icon-initial");
-
- private static long THYMELEAF_EXPIRE_TIME_MILLI_SECOND = 1 * 1000;
-
- private static int TRIM_THRESHOLD_OF_GAME_STATE_TABLE = 30000;
-
- private final DataSource memDbDataSource;
- private final DataSource fileDbDataSource;
-
private final Javalin app;
- private final ProblemsTable problemsTable;
- private final HandUpsTable handsUpTable;
- private final UsersTable usersTable;
- private final PasswordsTable passwordsTable;
- private final MatchingRequestsTable matchingRequestsTable;
- private final GameStatesTables gameStatesTables;
- private final VotesTable votesTable;
- private final WebsoketSessionsTable websoketSessionsTable;
- private final GameRecordsTable gameRecordsTable;
- private final LoginsTable loginsTable;
- private final WebsocketSessionsManager wsManager;
-
-
-
- static {
- }
public static void main(String[] args) {
- if (args.length != 0) {
- THYMELEAF_EXPIRE_TIME_MILLI_SECOND = Long.valueOf(args[0]);
- }
+
int port = 4567;
log.info("start (port:{}) => {}", port, SystemPropertyUtils.getJavaProperties());
ProcessUtils.stopProcessBindingPortIfExists(port);
- H2ServerUtils.startDefaultTcpServerProcessAndWaitFor();
- H2ServerUtils.startDefaultWebConsoleServerProcessAndWaitFor();
+ new H2TcpServerProcess(H2TcpServerProperties.builder().build()).awaitStart();
new GoApplication().start(port);
}
@@ -124,148 +47,54 @@ private void start(int port) {
}
public GoApplication() {
- FileDatabaseConfigJson fileDbConf = getFileDbConfig();
- H2LocalDataSourceFactory factory =
- H2LocalDataSourceFactory.builder(fileDbConf.databaseDirectory, fileDbConf.databaseName,
- fileDbConf.username, fileDbConf.password).build();
+ final long THYMELEAF_EXPIRE_TIME_MILLI_SECOND = 1 * 1000;
- this.memDbDataSource = createH2DataSource(factory.getInMemoryModeJdbcUrl(),
- factory.getUsername(), factory.getPassword());
- log.info("server jdbcUrl={}", factory.getServerModeJdbcUrl());
- this.fileDbDataSource = createHikariDataSource(factory.getServerModeJdbcUrl(),
- factory.getUsername(), factory.getPassword());
- // H2Server.openBrowser(memDbDataSource, true);
+ log.info("log4j2.configurationFile={}, Logger level={}",
+ System.getProperty("log4j2.configurationFile"), log.getLevel());
- TemplateEngine engine = new TemplateEngineBuilder().setPrefix("/templates/")
+ TemplateEngine engine = ThymeleafTemplateEnginBuilder.builder()
.setTtlMs(THYMELEAF_EXPIRE_TIME_MILLI_SECOND).build();
- engine.addDialect(new Java8TimeDialect());
- JavalinThymeleaf.configure(engine);
+ JavalinThymeleaf.init(engine);
- this.app = Javalin.create(config -> {
- config.addStaticFiles(WEBROOT_DIR_NAME, Location.CLASSPATH);
- config.autogenerateEtags = true;
- // config.precompressStaticFiles = true;
- config.enableCorsForAllOrigins();
- });
+ DataSourceManager basicDataSource = new DataSourceManager();
- {
- this.problemsTable = new ProblemsTable(memDbDataSource);
- problemsTable.dropAndInsertInitialProblemsToTable(PROBLEM_DIR);
- }
- {
- this.loginsTable = new LoginsTable(fileDbDataSource);
- loginsTable.createTableIfNotExists();
- loginsTable.createIndexesIfNotExists();
- loginsTable.writeCsv(new File(BACKUP_DIR, "logins-" + System.currentTimeMillis() + ".csv"));
- }
-
- this.handsUpTable = new HandUpsTable(memDbDataSource);
- {
- this.usersTable = new UsersTable(fileDbDataSource);
- usersTable.dropTableIfExists();
- usersTable.createTableAndIndexesIfNotExists();
- try {
- File f = ResourceUtils.getResourceAsFile("/conf/users.csv");
- usersTable.readFromFileAndMerge(f);
- } catch (Exception e) {
- log.error(e, e);
- log.warn("load users.csv.default ...");
- File f = ResourceUtils.getResourceAsFile("/conf/users.csv.default");
- usersTable.readFromFileAndMerge(f);
- }
- }
- {
- this.passwordsTable = new PasswordsTable(fileDbDataSource);
- passwordsTable.createTableIfNotExists().createIndexesIfNotExists();
- try {
- File f = ResourceUtils.getResourceAsFile("/conf/passwords.csv");
- passwordsTable.readFromFileAndMerge(f);
- } catch (Exception e) {
- log.warn("load password.csv.default ...");
- File f = ResourceUtils.getResourceAsFile("/conf/passwords.csv.default");
- passwordsTable.readFromFileAndMerge(f);
- }
- }
- {
- this.gameRecordsTable = new GameRecordsTable(fileDbDataSource);
- gameRecordsTable.createTableIfNotExists().createIndexesIfNotExists();
- gameRecordsTable
- .writeCsv(new File(BACKUP_DIR, "game-record" + System.currentTimeMillis() + ".csv"));
-
- gameRecordsTable.recalculateAndUpdateRank(usersTable);
- }
- {
- this.matchingRequestsTable = new MatchingRequestsTable(memDbDataSource);
- matchingRequestsTable.createTableIfNotExists().createIndexesIfNotExists();
- }
- {
-
- GameStatesTable gameStatesTable = new GameStatesTable(fileDbDataSource);
- gameStatesTable.createTableIfNotExists().createIndexesIfNotExists();
-
- gameStatesTable.trimAndBackupToFile(factory.getDatabaseDirectory(),
- TRIM_THRESHOLD_OF_GAME_STATE_TABLE);
-
- GameStatesTable gameStatesTableInMem = new GameStatesTable(memDbDataSource);
- gameStatesTableInMem.createTableIfNotExists().createIndexesIfNotExists();
- gameStatesTableInMem.insert(gameStatesTable.selectAll().toArray(GameState[]::new));
-
- this.gameStatesTables = new GameStatesTables(gameStatesTable, gameStatesTableInMem);
- }
- {
- this.votesTable = new VotesTable(memDbDataSource);
- votesTable.createTableIfNotExists().createIndexesIfNotExists();
- }
- {
- this.websoketSessionsTable = new WebsoketSessionsTable(memDbDataSource);
- this.websoketSessionsTable.createTableIfNotExists().createIndexesIfNotExists();
- }
- this.wsManager = new WebsocketSessionsManager(gameStatesTables, problemsTable,
- websoketSessionsTable, usersTable, handsUpTable, matchingRequestsTable);
-
-
-
- prepareWebSocket();
- prepareJsonRpc();
- prepareGetHandler();
- }
+ GoTables goTables = GoTables.prepareTables(basicDataSource);
+ WebsocketSessionsManager webSocketManager =
+ new WebsocketSessionsManager(goTables, basicDataSource.createHikariInMemoryDataSource());
- private FileDatabaseConfigJson getFileDbConfig() {
- try {
- return getDefaultJacksonMapper().toObject(ResourceUtils.getResourceAsFile("/conf/h2.json"),
- FileDatabaseConfigJson.Builder.class).build();
- } catch (Exception e) {
- log.warn("Try to load h2.json.default");
- return getDefaultJacksonMapper()
- .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json.default"),
- FileDatabaseConfigJson.Builder.class)
- .build();
- }
- }
+ scheduleCheckMatchingRequest(webSocketManager, goTables);
- public static String getJdbcUrlOfInMemoryDb(String dbName) {
- return "jdbc:h2:mem:" + dbName + ";DB_CLOSE_DELAY=-1";
- }
+ FirebaseAuthHandler firebaseService = BasicFirebaseAuthHandler.create(
+ goTables.usersTable.readAll().stream().map(u -> u.email()).toList(),
+ ResourceUtils.getResourceAsFile("/conf/firebase.json"));
+ GoAuthService authService = new GoAuthService(goTables.usersTable, firebaseService);
- private void prepareWebSocket() {
- app.ws("/websocket/play/checkcon", ws -> {
- ws.onConnect(ctx -> {
- log.debug("{}", ctx.session.getUpgradeRequest().getRequestURI());
- });
- });
- app.ws("/websocket/play", ws -> {
- ws.onConnect(ctx -> wsManager.onConnect(ctx.session, ctx.queryParam("userId"),
- ctx.queryParam("gameId")));
- ws.onClose(ctx -> wsManager.onClose(ctx.session, ctx.status(), ctx.reason()));
- ws.onError(ctx -> wsManager.onError(ctx.session, ctx.error()));
- ws.onMessage(ctx -> wsManager.onMessage(ctx.queryParam("gameId"), ctx));
+ this.app = Javalin.create(config -> {
+ config.staticFiles.add(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory().getName(),
+ Location.CLASSPATH);
+ config.staticFiles.enableWebjars();
+ config.http.generateEtags = true;
+ config.plugins.enableCors(cors -> cors.add(corsConfig -> corsConfig.anyHost()));
+ config.accessManager(new GoAccessManager(goTables.usersTable, authService));
});
+
+ prepareWebSocket(app, webSocketManager);
+ prepareJsonRpc(app, webSocketManager, new GoJsonRpcService(webSocketManager, goTables),
+ new AuthService.Factory(goTables.usersTable, goTables.loginsTable, goTables.passwordsTable,
+ authService));
+
+
+ GoAppHandlers.prepareGetHandler(app, webSocketManager, goTables, authService);
+ }
+
+ private static void scheduleCheckMatchingRequest(WebsocketSessionsManager webSocketManager,
+ GoTables goTables) {
final int INTERVAL_IN_WAITING_ROOM = 10;
ScheduledExecutorService srv = Executors.newSingleThreadScheduledExecutor(runnable -> {
@@ -273,232 +102,42 @@ private void prepareWebSocket() {
t.setDaemon(true);
return t;
});
+
srv.scheduleWithFixedDelay(Try.createRunnable(() -> {
- Set uids = matchingRequestsTable.createPairOfUsers(gameStatesTables);
- wsManager.sendUpdateWaitingRequestStatus(uids);
+ Set uids =
+ goTables.matchingRequestsTable.createPairOfUsers(goTables.gameStatesTables);
+ webSocketManager.sendUpdateWaitingRequestStatus(uids);
}, e -> log.error(e)), INTERVAL_IN_WAITING_ROOM, INTERVAL_IN_WAITING_ROOM, TimeUnit.SECONDS);
- }
-
- private void prepareJsonRpc() {
-
- prepareFirebase();
-
- final GoJsonRpcService goJsonRpcService = new GoJsonRpcService(wsManager, gameStatesTables,
- problemsTable, usersTable, loginsTable, matchingRequestsTable, votesTable, handsUpTable,
- websoketSessionsTable, gameRecordsTable);
-
- JacksonMapper mapper = GoApplication.getDefaultJacksonMapper();
- JsonRpcService jsonRpcService = new JsonRpcService(mapper);
-
- app.post("/app/json/GoJsonRpcService", ctx -> {
- JsonRpcRequest jreq = jsonRpcService.toJsonRpcRequest(ctx.req);
- Object srv = AuthServiceInterface.getDeclaredMethodNames().contains(jreq.getMethod())
- ? new AuthService(usersTable, loginsTable, passwordsTable, ctx.req)
- : goJsonRpcService;
- JsonRpcResponse jres = jsonRpcService.callHttpJsonRpc(srv, jreq, ctx.res);
- String ret = mapper.toJson(jres);
- ctx.result(ret).contentType("application/json");
- });
}
- private boolean prepareFirebase() {
- try {
- String url = Files
- .readAllLines(ResourceUtils.getResourceAsFile("/conf/firebase-url.conf").toPath()).get(0);
- AuthService.initialize(url, ResourceUtils.getResourceAsFile("/conf/firebase.json"));
- return true;
- } catch (Exception e) {
- log.warn("Skip firebase settings");
- return false;
- }
- }
-
- private void prepareGetHandler() {
- app.get("/app", ctx -> ctx.redirect("/app/index.html"));
-
- app.get("/app/", ctx -> {
- String pageName =
- ctx.pathParam("pageName") == null ? "index.html" : ctx.pathParam("pageName");
- Builder model = createDefaultModel(usersTable, ctx.req);
- switch (pageName) {
- case "play.html" -> {
- UserSession session = UserSession.wrap(ctx.req.getSession());
- if (!session.isLogined()) {
- model.put("requireToLogin", true);
- break;
- }
- session.getUserId().ifPresent(uid -> {
- boolean attend = loginsTable.isAttendance(uid);
- model.put("isAttendance", attend);
- model.put("problemGroupsJson", problemsTable.getProblemGroupsNode());
- });
- }
- case "players-all.html" -> {
- try {
- isAdminOrThrow(usersTable, ctx.req);
- } catch (Exception e) {
- ctx.redirect("/app/index.html");
- log.error(e.getMessage());
- return;
- }
- List> users = usersTable.readAllWithLastLogin();
- List loginJsons = users.stream().map(t -> new LoginJson(t.getT2(), t.getT1()))
- .collect(Collectors.toList());
- model.put("userAccounts", loginJsons);
- pageName = "players.html";
- }
- case "players.html" -> {
- try {
- isAdminOrThrow(usersTable, ctx.req);
- } catch (Exception e) {
- ctx.redirect("/app/index.html");
- log.error(e.getMessage());
- return;
- }
- List> users = usersTable.readAllWithLastLogin();
- List loginJsons = users.stream().filter(t -> t.getT1().isStudent())
- .map(t -> new LoginJson(t.getT2(), t.getT1())).collect(Collectors.toList());
- model.put("userAccounts", loginJsons);
- }
- case "games-all.html" -> {
- List tmp = gameStatesTables.readTodayGameJsons().stream().map(gsj -> {
- String gameId = gsj.gameId();
- GameStateViewJson json =
- new GameStateViewJson(gsj, handsUpTable.selectByPrimaryKey(gameId),
- websoketSessionsTable.getWatchingUniqueStudentsNum(usersTable, gameId));
- return json;
- }).collect(Collectors.toList());
- model.put("games", tmp);
- pageName = "games.html";
- }
- case "games.html" -> {
- List gids = websoketSessionsTable.readActiveGameIdsOrderByGameId(usersTable);
- List tmp =
- gids.stream().map(gid -> gameStatesTables.readLatestGameStateJson(gid)).map(gsj -> {
- String gameId = gsj.gameId();
- GameStateViewJson json =
- new GameStateViewJson(gsj, handsUpTable.selectByPrimaryKey(gameId),
- websoketSessionsTable.getWatchingUniqueStudentsNum(usersTable, gameId));
- return json;
- }).collect(Collectors.toList());
- model.put("games",
- tmp.stream().filter(j -> j.watchingStudentsNum() > 0).collect(Collectors.toList()));
- }
- case "fragment/game-record-table.html" -> {
- String userId = ctx.queryParam("userId");
- List records = gameRecordsTable.readByUserId(userId);
- model.put("records", records);
- }
- case "fragment/question-table.html", "fragment/question-table-small.html" -> {
- List gids = handsUpTable.readAllGameIds();
- List tmp =
- gameStatesTables.readLatestBoardsJson(gids).stream().map(gsj -> {
- String gameId = gsj.gameId();
- GameStateViewJson json =
- new GameStateViewJson(gsj, handsUpTable.selectByPrimaryKey(gameId), 0);
- return json;
- }).collect(Collectors.toList());
- model.put("games", tmp);
- }
- case "fragment/waiting-request-table.html" -> {
- String userId = ctx.queryParam("userId");
- if (userId != null) {
- List tmp = matchingRequestsTable.readRequests();
- model.put("requests",
- tmp.stream().filter(r -> r.userId().equals(userId)).collect(Collectors.toList()));
- } else {
- model.put("requests", matchingRequestsTable.readRequests());
- }
- }
- case "fragment/waiting-request-table-small.html" -> {
- String userId = ctx.queryParam("userId");
- List tmp = matchingRequestsTable.readRequests();
- MatchingRequest req = tmp.stream().filter(r -> r.userId().equals(userId)).findAny()
- .orElse(new MatchingRequest());
- model.put("req", req);
- model.put("reqNum", tmp.size());
- }
- }
- ctx.render(pageName, model.build().getMap());
+ private static void prepareWebSocket(Javalin app, WebsocketSessionsManager webSocketManager) {
+ final int WS_PING_INTERVAL_SEC = 27;
+ app.ws("/websocket/play/checkcon", ws -> ws
+ .onConnect(ctx -> log.trace("{}", ctx.session.getUpgradeRequest().getRequestURI())));
+ app.ws("/websocket/play", ws -> {
+ ws.onConnect(ctx -> {
+ webSocketManager.onConnect(ctx.session, ctx.queryParam("userId"), ctx.queryParam("gameId"));
+ ctx.enableAutomaticPings(WS_PING_INTERVAL_SEC, TimeUnit.SECONDS);
+ });
+ ws.onClose(ctx -> webSocketManager.onClose(ctx.session, ctx.status(), ctx.reason()));
+ ws.onError(ctx -> webSocketManager.onError(ctx.session, ctx.error()));
+ ws.onMessage(ctx -> webSocketManager.onMessage(ctx.queryParam("gameId"), ctx));
});
}
- private static final int DEFAULT_MAX_CONNECTIONS =
- Math.min(ForkJoinPoolUtils.availableProcessors() * 2 * 2, 10);
- private static final int DEFAULT_TIMEOUT_SECONDS = 30;
-
- private JdbcConnectionPool createH2DataSource(String url, String user, String password) {
- JdbcConnectionPool ds = JdbcConnectionPool.create(url, user, password);
- ds.setMaxConnections(DEFAULT_MAX_CONNECTIONS);
- ds.setLoginTimeout(DEFAULT_TIMEOUT_SECONDS);
- return ds;
- }
-
- public static HikariDataSource createHikariDataSource(String url, String user, String password) {
- HikariConfig config = new HikariConfig();
- config.setJdbcUrl(url);
- config.setUsername(user);
- config.setPassword(password);
- config.setMaximumPoolSize(DEFAULT_MAX_CONNECTIONS);
- config.setConnectionTimeout(DEFAULT_TIMEOUT_SECONDS * 1000);
- config.addDataSourceProperty("useServerPrepStmts", "true");
- config.addDataSourceProperty("cachePrepStmts", "true");
- config.addDataSourceProperty("prepStmtCacheSize", "250");
- config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
- config.addDataSourceProperty("minimumIdle", "2048");
- return new HikariDataSource(config);
- }
-
-
-
- private Builder createDefaultModel(UsersTable usersTable, HttpServletRequest request) {
- ViewModel.Builder modelBuilder =
- ViewModel.builder().setFileModifiedDate(WEBROOT_DIR, 10, "js", "css");
- modelBuilder.put("currentUser", getCurrentUserAccount(usersTable, request));
- return modelBuilder;
+ private static void prepareJsonRpc(Javalin app, WebsocketSessionsManager webSocketManager,
+ GoJsonRpcService jsonRpcSrv, AuthService.Factory authServiceFactory) {
+ JavalinJsonRpcService srv = new JavalinJsonRpcService(GoApplication.getDefaultJacksonMapper());
+ app.post("/app/json/GoJsonRpcService", ctx -> srv.handle(ctx, jsonRpcSrv));
+ app.post("/app/json/AuthRpcService",
+ ctx -> srv.handle(ctx, authServiceFactory.create(ctx.req())));
}
-
- private User getCurrentUserAccount(UsersTable usersTable, HttpServletRequest request) {
- Optional u = UserSession.wrap(request.getSession()).getUserId()
- .map(uid -> usersTable.selectByPrimaryKey(uid));
- return u.orElse(new User());
- }
-
- private void isAdminOrThrow(UsersTable usersTable, HttpServletRequest req) {
- FirebaseUserSession session = FirebaseUserSession.wrap(req.getSession());
- User u = null;
- if (session.isSigninFirebase()) {
- String email = session.getEmail();
- u = usersTable.readByEmail(email);
- } else if (session.isLogined()) {
- u = session.getUserId().map(userId -> usersTable.selectByPrimaryKey(userId)).orElse(null);
- }
- if (u == null) {
- throw new RuntimeException(ParameterizedStringUtils.newString("User not found"));
- }
- if (!u.isAdmin()) {
- throw new RuntimeException(ParameterizedStringUtils.newString("User is not admin"));
- }
-
-
- }
-
- public static record LoginJson(Login login, User user) {
-
- }
-
public static JacksonMapper getDefaultJacksonMapper() {
return JacksonMapper.getIgnoreUnknownPropertiesMapper();
}
- public static record GameStateViewJson(GameStateJson gameState, HandUp handUp,
- int watchingStudentsNum) {
-
- }
-
-
}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoTables.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoTables.java
new file mode 100644
index 0000000..12a34ae
--- /dev/null
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoTables.java
@@ -0,0 +1,159 @@
+package org.nkjmlab.go.javalin;
+
+import java.io.File;
+import javax.sql.DataSource;
+import org.nkjmlab.go.javalin.model.relation.GameRecordsTable;
+import org.nkjmlab.go.javalin.model.relation.GameStatesTable;
+import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState;
+import org.nkjmlab.go.javalin.model.relation.GameStatesTables;
+import org.nkjmlab.go.javalin.model.relation.HandUpsTable;
+import org.nkjmlab.go.javalin.model.relation.LoginsTable;
+import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable;
+import org.nkjmlab.go.javalin.model.relation.PasswordsTable;
+import org.nkjmlab.go.javalin.model.relation.ProblemsTable;
+import org.nkjmlab.go.javalin.model.relation.UsersTable;
+import org.nkjmlab.go.javalin.model.relation.VotesTable;
+import org.nkjmlab.util.java.io.SystemFileUtils;
+import org.nkjmlab.util.java.lang.ResourceUtils;
+
+public class GoTables {
+
+ private static final org.apache.logging.log4j.Logger log =
+ org.apache.logging.log4j.LogManager.getLogger();
+
+ public final GameStatesTables gameStatesTables;
+ public final ProblemsTable problemsTable;
+ public final UsersTable usersTable;
+ public final LoginsTable loginsTable;
+ public final MatchingRequestsTable matchingRequestsTable;
+ public final VotesTable votesTable;
+ public final HandUpsTable handsUpTable;
+ public final GameRecordsTable gameRecordsTable;
+ public final PasswordsTable passwordsTable;
+
+ private GoTables(GameStatesTables gameStatesTables, ProblemsTable problemsTable,
+ UsersTable usersTable, PasswordsTable passwordsTable, LoginsTable loginsTable,
+ MatchingRequestsTable matchingRequestsTable, VotesTable votesTable, HandUpsTable handsUpTable,
+ GameRecordsTable gameRecordsTable) {
+ this.gameStatesTables = gameStatesTables;
+ this.problemsTable = problemsTable;
+ this.usersTable = usersTable;
+ this.passwordsTable = passwordsTable;
+ this.loginsTable = loginsTable;
+ this.matchingRequestsTable = matchingRequestsTable;
+ this.votesTable = votesTable;
+ this.handsUpTable = handsUpTable;
+ this.gameRecordsTable = gameRecordsTable;
+ }
+
+ public static GoTables prepareTables(DataSourceManager basicDataSource) {
+
+ DataSource memDbDataSource = basicDataSource.createHikariInMemoryDataSource();
+ DataSource fileDbDataSource = basicDataSource.createHikariServerModeDataSource();
+
+
+ final ProblemsTable problemsTable = prepareProblemTables(memDbDataSource);
+ final HandUpsTable handsUpTable = new HandUpsTable(memDbDataSource);
+ final MatchingRequestsTable matchingRequestsTable =
+ prepareMatchingRequestsTable(memDbDataSource);
+ final PasswordsTable passwordsTable = preparePasswordsTable(memDbDataSource);
+ final VotesTable votesTable = prepareVotesTable(memDbDataSource);
+
+ final GameStatesTables gameStatesTables =
+ prepareGameStateTables(basicDataSource, fileDbDataSource, memDbDataSource);
+
+ final UsersTable usersTable = prepareUsersTable(fileDbDataSource);
+ final GameRecordsTable gameRecordsTable = prepareGameRecordsTable(fileDbDataSource, usersTable);
+ final LoginsTable loginsTable = prepareLoginsTable(fileDbDataSource);
+
+
+ GoTables goTables = new GoTables(gameStatesTables, problemsTable, usersTable, passwordsTable,
+ loginsTable, matchingRequestsTable, votesTable, handsUpTable, gameRecordsTable);
+
+ return goTables;
+ }
+
+ private static GameRecordsTable prepareGameRecordsTable(DataSource fileDbDataSource,
+ UsersTable usersTable) {
+ GameRecordsTable gameRecordsTable = new GameRecordsTable(fileDbDataSource);
+ gameRecordsTable.createTableIfNotExists().createIndexesIfNotExists();
+ gameRecordsTable.writeCsv(new File(new File(SystemFileUtils.getUserHomeDirectory(), "go-bkup/"),
+ "game-record" + System.currentTimeMillis() + ".csv"));
+ gameRecordsTable.recalculateAndUpdateRank(usersTable);
+ return gameRecordsTable;
+ }
+
+ private static VotesTable prepareVotesTable(DataSource memDbDataSource) {
+ VotesTable votesTable = new VotesTable(memDbDataSource);
+ votesTable.createTableIfNotExists().createIndexesIfNotExists();
+ return votesTable;
+ }
+
+ private static MatchingRequestsTable prepareMatchingRequestsTable(DataSource memDbDataSource) {
+ MatchingRequestsTable matchingRequestsTable = new MatchingRequestsTable(memDbDataSource);
+ matchingRequestsTable.createTableIfNotExists().createIndexesIfNotExists();
+ return matchingRequestsTable;
+ }
+
+ private static GameStatesTables prepareGameStateTables(DataSourceManager basicDataSource,
+ DataSource fileDbDataSource, DataSource memDbDataSource) {
+ final int TRIM_THRESHOLD_OF_GAME_STATE_TABLE = 30000;
+
+ GameStatesTable gameStatesTable = new GameStatesTable(fileDbDataSource);
+ gameStatesTable.createTableIfNotExists().createIndexesIfNotExists();
+ gameStatesTable.trimAndBackupToFile(basicDataSource.getFactory().getDatabaseDirectory(),
+ TRIM_THRESHOLD_OF_GAME_STATE_TABLE);
+
+ GameStatesTable gameStatesTableInMem = new GameStatesTable(memDbDataSource);
+ gameStatesTableInMem.createTableIfNotExists().createIndexesIfNotExists();
+ gameStatesTableInMem.insert(gameStatesTable.selectAll().toArray(GameState[]::new));
+
+ GameStatesTables gameStatesTables = new GameStatesTables(gameStatesTable, gameStatesTableInMem);
+ return gameStatesTables;
+ }
+
+ private static PasswordsTable preparePasswordsTable(DataSource dataSource) {
+ PasswordsTable passwordsTable = new PasswordsTable(dataSource);
+ passwordsTable.createTableIfNotExists().createIndexesIfNotExists();
+ try {
+ File f = ResourceUtils.getResourceAsFile("/conf/passwords.csv");
+ passwordsTable.readFromFileAndMerge(f);
+ } catch (Exception e) {
+ log.warn("load password.csv.default ...");
+ File f = ResourceUtils.getResourceAsFile("/conf/passwords.csv.default");
+ passwordsTable.readFromFileAndMerge(f);
+ }
+ return passwordsTable;
+ }
+
+ private static LoginsTable prepareLoginsTable(DataSource fileDbDataSource) {
+ LoginsTable loginsTable = new LoginsTable(fileDbDataSource);
+ loginsTable.createTableIfNotExists().createIndexesIfNotExists();
+ loginsTable.writeCsv(new File(new File(SystemFileUtils.getUserHomeDirectory(), "go-bkup/"),
+ "logins-" + System.currentTimeMillis() + ".csv"));
+ return loginsTable;
+ }
+
+ private static UsersTable prepareUsersTable(DataSource dataSource) {
+ UsersTable usersTable = new UsersTable(dataSource);
+ usersTable.dropTableIfExists();
+ usersTable.createTableIfNotExists().createIndexesIfNotExists();
+ try {
+ File f = ResourceUtils.getResourceAsFile("/conf/users.csv");
+ usersTable.readFromFileAndMerge(f);
+ } catch (Exception e) {
+ log.error(e, e);
+ log.warn("load users.csv.default ...");
+ File f = ResourceUtils.getResourceAsFile("/conf/users.csv.default");
+ usersTable.readFromFileAndMerge(f);
+ }
+ return usersTable;
+ }
+
+ private static ProblemsTable prepareProblemTables(DataSource memDbDataSource) {
+ ProblemsTable problemsTable = new ProblemsTable(memDbDataSource);
+ problemsTable.dropAndInsertInitialProblemsToTable(GoWebAppConfig.PROBLEM_DIR);
+ return problemsTable;
+ }
+
+}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoWebAppConfig.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoWebAppConfig.java
new file mode 100644
index 0000000..e5dc517
--- /dev/null
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoWebAppConfig.java
@@ -0,0 +1,18 @@
+package org.nkjmlab.go.javalin;
+
+import java.io.File;
+import org.nkjmlab.util.java.web.WebApplicationConfig;
+
+public class GoWebAppConfig {
+ public static final WebApplicationConfig WEB_APP_CONFIG = WebApplicationConfig.builder()
+ .addWebJar("jquery", "sweetalert2", "bootstrap", "bootstrap-treeview", "clipboard",
+ "fortawesome__fontawesome-free", "stacktrace-js", "datatables", "firebase",
+ "firebaseui", "ua-parser-js", "blueimp-load-image", "emojionearea")
+ .build();
+ public static final File PROBLEM_DIR =
+ new File(GoWebAppConfig.WEB_APP_CONFIG.getAppRootDirectory(), "problem");
+ public static final File CURRENT_ICON_DIR =
+ new File(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), "img/icon");
+ public static final File UPLOADED_ICON_DIR =
+ new File(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), "img/icon-uploaded");
+}
\ No newline at end of file
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/AuthService.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthService.java
similarity index 50%
rename from nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/AuthService.java
rename to nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthService.java
index a3bd3f7..62660fb 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/AuthService.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthService.java
@@ -1,10 +1,8 @@
-package org.nkjmlab.go.javalin.fbauth;
+package org.nkjmlab.go.javalin.auth;
-import java.io.File;
-import java.io.FileInputStream;
import java.time.LocalDateTime;
import java.util.Optional;
-import javax.servlet.http.HttpServletRequest;
+import org.nkjmlab.go.javalin.auth.GoAuthService.SigninSession;
import org.nkjmlab.go.javalin.model.relation.LoginsTable;
import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login;
import org.nkjmlab.go.javalin.model.relation.PasswordsTable;
@@ -12,43 +10,56 @@
import org.nkjmlab.go.javalin.model.relation.UsersTable.User;
import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson;
import org.nkjmlab.sorm4j.result.RowMap;
-import org.nkjmlab.util.javax.servlet.HttpRequestUtils;
-import org.nkjmlab.util.javax.servlet.UserSession;
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.firebase.FirebaseApp;
-import com.google.firebase.FirebaseOptions;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.FirebaseAuthException;
-import com.google.firebase.auth.FirebaseToken;
+import org.nkjmlab.util.jakarta.servlet.HttpRequestUtils;
+import jakarta.servlet.http.HttpServletRequest;
public class AuthService implements AuthServiceInterface {
+
+ public static class Factory {
+ private final UsersTable usersTable;
+ private final LoginsTable loginsTable;
+ private final PasswordsTable passwordsTable;
+ private final GoAuthService firebaseService;
+
+ public Factory(UsersTable usersTable, LoginsTable loginsTable, PasswordsTable passwordsTable,
+ GoAuthService firebaseService) {
+ this.usersTable = usersTable;
+ this.loginsTable = loginsTable;
+ this.passwordsTable = passwordsTable;
+ this.firebaseService = firebaseService;
+ }
+
+ public AuthService create(HttpServletRequest request) {
+ return new AuthService(usersTable, loginsTable, passwordsTable, firebaseService, request);
+ }
+
+ }
+
private static final org.apache.logging.log4j.Logger log =
org.apache.logging.log4j.LogManager.getLogger();
private final UsersTable usersTable;
private final LoginsTable loginsTable;
private final PasswordsTable passwordsTable;
+ private final GoAuthService authService;
private final HttpServletRequest request;
-
- public AuthService(UsersTable usersTable, LoginsTable loginsTable, PasswordsTable passwordsTable,
- HttpServletRequest request) {
+ private AuthService(UsersTable usersTable, LoginsTable loginsTable, PasswordsTable passwordsTable,
+ GoAuthService firebaseService, HttpServletRequest request) {
this.usersTable = usersTable;
this.loginsTable = loginsTable;
this.passwordsTable = passwordsTable;
+ this.authService = firebaseService;
this.request = request;
}
@Override
public boolean isSigninToFirebase() {
- FirebaseUserSession session = FirebaseUserSession.wrap(request.getSession());
- return session.isSigninFirebase();
+ return authService.isSignin(request.getSession().getId());
}
@Override
public boolean registerAttendance(String userId, String seatId) {
- UserSession session = UserSession.wrap(request.getSession());
- session.setUserId(userId);
User u = usersTable.selectByPrimaryKey(userId);
usersTable.updateByPrimaryKey(RowMap.of("seat_id", seatId), u.userId());
loginsTable.insert(new Login(-1, userId, seatId, u.userName(), LocalDateTime.now(),
@@ -59,11 +70,12 @@ public boolean registerAttendance(String userId, String seatId) {
@Override
public UserJson signinWithFirebase(String idToken, String seatId) {
- return verifyIdToken(idToken).map(token -> usersTable.readByEmail(token.getEmail())).map(u -> {
- FirebaseUserSession session = FirebaseUserSession.wrap(request.getSession());
- session.signinFirebase(idToken, u.email());
- session.setUserId(u.userId());
- registerAttendance(u.userId(), seatId);
+ Optional opt =
+ authService.signinWithFirebase(idToken, request.getSession().getId());
+ return opt.map(ls -> {
+ User u = usersTable.selectByPrimaryKey(ls.userId());
+ registerAttendance(ls.userId(), seatId);
+ authService.signin(request.getSession().getId(), ls.userId());
return new UserJson(u, true);
}).orElseThrow();
}
@@ -71,22 +83,21 @@ public UserJson signinWithFirebase(String idToken, String seatId) {
@Override
public boolean signoutFromFirebase() {
- request.getSession().invalidate();
+ authService.signout(request.getSession().getId());
return true;
}
@Override
public boolean signupAsGuest(String userId, String username, String seatId) {
- FirebaseUserSession session = FirebaseUserSession.wrap(request.getSession());
- if (session.isSigninFirebase()) {
+ if (authService.isSignin(request.getSession().getId())) {
log.error("Already logined Firebase. userId=[{}]", userId);
return false;
}
User u = usersTable.selectByPrimaryKey(userId);
if (u != null && !u.isGuest()) {
- log.error("Try guest siginup but userId [{}] conflict with a regular user", userId);
+ log.error("Try guest signinup but userId [{}] conflict with a regular user", userId);
return false;
}
usersTable.merge(new User(userId, userId + "-guest@example.com", username, User.GUEST, seatId,
@@ -94,13 +105,13 @@ public boolean signupAsGuest(String userId, String username, String seatId) {
registerAttendance(userId, seatId);
UsersTable.createIcon(userId);
+ authService.signin(request.getSession().getId(), userId);
return true;
}
@Override
public UserJson signinWithoutFirebase(String userId, String password, String seatId) {
- FirebaseUserSession session = FirebaseUserSession.wrap(request.getSession());
- if (session.isSigninFirebase()) {
+ if (authService.isSignin(request.getSession().getId())) {
log.error("Already logined Firebase. userId=[{}]", userId);
return null;
}
@@ -112,32 +123,9 @@ public UserJson signinWithoutFirebase(String userId, String password, String sea
User u = usersTable.selectByPrimaryKey(userId);
registerAttendance(userId, seatId);
UsersTable.createIcon(userId);
+ authService.signin(request.getSession().getId(), userId);
return new UserJson(u, true);
}
- public static void initialize(String url, File firebaseJson) {
- try (FileInputStream serviceAccount = new FileInputStream(firebaseJson)) {
- FirebaseOptions options = FirebaseOptions.builder()
- .setCredentials(GoogleCredentials.fromStream(serviceAccount)).setDatabaseUrl(url).build();
- FirebaseApp.initializeApp(options);
- } catch (Exception e) {
- log.error(e, e);
- }
-
- }
-
- public static Optional verifyIdToken(String idToken) {
- try {
- if (idToken == null || idToken.length() == 0) {
- return Optional.empty();
- }
- FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
- return decodedToken.isEmailVerified() ? Optional.of(decodedToken) : Optional.empty();
- } catch (FirebaseAuthException e) {
- log.error(e, e);
- return Optional.empty();
- }
- }
-
}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/AuthServiceInterface.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthServiceInterface.java
similarity index 57%
rename from nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/AuthServiceInterface.java
rename to nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthServiceInterface.java
index fb49816..081298a 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/AuthServiceInterface.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthServiceInterface.java
@@ -1,8 +1,5 @@
-package org.nkjmlab.go.javalin.fbauth;
+package org.nkjmlab.go.javalin.auth;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson;
public interface AuthServiceInterface {
@@ -19,10 +16,5 @@ public interface AuthServiceInterface {
boolean registerAttendance(String userId, String seatId);
- static Set getDeclaredMethodNames() {
- return Arrays.stream(AuthServiceInterface.class.getDeclaredMethods()).map(m -> m.getName())
- .collect(Collectors.toSet());
- }
-
}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/GoAuthService.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/GoAuthService.java
new file mode 100644
index 0000000..278cc20
--- /dev/null
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/GoAuthService.java
@@ -0,0 +1,84 @@
+package org.nkjmlab.go.javalin.auth;
+
+import java.util.Optional;
+import org.nkjmlab.go.javalin.model.relation.UsersTable;
+import org.nkjmlab.go.javalin.model.relation.UsersTable.User;
+import org.nkjmlab.sorm4j.Sorm;
+import org.nkjmlab.sorm4j.annotation.OrmRecord;
+import org.nkjmlab.sorm4j.util.h2.BasicH2Table;
+import org.nkjmlab.sorm4j.util.h2.datasource.H2LocalDataSourceFactory;
+import org.nkjmlab.sorm4j.util.table_def.annotation.PrimaryKey;
+import org.nkjmlab.util.firebase.auth.FirebaseAuthHandler;
+import com.google.firebase.auth.FirebaseToken;
+
+public class GoAuthService {
+ private static final org.apache.logging.log4j.Logger log =
+ org.apache.logging.log4j.LogManager.getLogger();
+
+ private final FirebaseAuthHandler firebaseService;
+ private final SigninSessionsTable signinSessionsTable = new SigninSessionsTable();
+ private final UsersTable usersTable;
+
+ public GoAuthService(UsersTable usersTable, FirebaseAuthHandler firebaseService) {
+ this.usersTable = usersTable;
+ this.firebaseService = firebaseService;
+ signinSessionsTable.createTableIfNotExists().createIndexesIfNotExists();
+ }
+
+ public Optional signinWithFirebase(String idToken, String sessionId) {
+ Optional opt = firebaseService.isAcceptableIdToken(idToken);
+ User u = opt.map(fs -> usersTable.readByEmail(fs.getEmail())).get();
+ if (u == null) {
+ log.error("invalid mail {}", opt.get().getEmail());
+ return Optional.empty();
+ }
+
+ SigninSession ret = opt.map(fs -> new SigninSession(sessionId, u.userId())).get();
+ signin(ret);
+ return Optional.of(ret);
+ }
+
+ public SigninSession signin(String sessionId, String userId) {
+ SigninSession s = new SigninSession(sessionId, userId);
+ signin(s);
+ return s;
+ }
+
+
+ private SigninSession signin(SigninSession signinSession) {
+ signinSessionsTable.merge(signinSession);
+ return signinSession;
+ }
+
+ public void signout(String sessionId) {
+ signinSessionsTable.deleteByPrimaryKey(sessionId);
+
+ }
+
+ public boolean isSignin(String sessionId) {
+ return signinSessionsTable.exists(sessionId);
+ }
+
+
+ public Optional toSigninSession(String sessionId) {
+ return isSignin(sessionId) ? Optional.of(signinSessionsTable.selectByPrimaryKey(sessionId))
+ : Optional.empty();
+ }
+
+ @OrmRecord
+ public record SigninSession(@PrimaryKey String sessionId, String userId) {
+
+ }
+
+ private static class SigninSessionsTable extends BasicH2Table {
+
+ public SigninSessionsTable() {
+ super(Sorm.create(H2LocalDataSourceFactory.createTemporalInMemoryDataSource()),
+ SigninSession.class);
+ }
+
+ }
+
+
+
+}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/client/websocket/GoWebSocketClient.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/client/websocket/GoWebSocketClient.java
deleted file mode 100644
index 1f26ce2..0000000
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/client/websocket/GoWebSocketClient.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.nkjmlab.go.javalin.client.websocket;
-
-import java.net.URI;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
-import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
-import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
-import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
-import org.eclipse.jetty.websocket.api.annotations.WebSocket;
-import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
-import org.eclipse.jetty.websocket.client.WebSocketClient;
-
-public class GoWebSocketClient {
- private static final org.apache.logging.log4j.Logger log =
- org.apache.logging.log4j.LogManager.getLogger();
- private static final int num = 64;
-
- private final String uri;
- private final WebSocketClient client = new WebSocketClient();
- private final GoWebSocket webSocket = new GoWebSocket();
-
- public GoWebSocketClient(String uri) {
- this.uri = uri;
- }
-
- public void start() {
- try {
- client.start();
-
- for (int i = 0; i < num; i++) {
- int stdId = 5519000 + i;
- URI toUri = new URI(uri + "?userId=" + stdId + "&gameId=" + stdId);
- client.connect(webSocket, toUri, new ClientUpgradeRequest());
- }
-
- } catch (Throwable t) {
- log.error(t, t);
- } finally {
- try {
- TimeUnit.SECONDS.sleep(20);
- client.stop();
- } catch (Exception e) {
- log.error(e, e);
- }
- }
- }
-
- @WebSocket
- public class GoWebSocket {
-
- private static final org.apache.logging.log4j.Logger log =
- org.apache.logging.log4j.LogManager.getLogger();
-
- private static AtomicInteger onConnectCounter = new AtomicInteger(0);
- private static AtomicInteger onMessageCounter = new AtomicInteger(0);
-
- @OnWebSocketConnect
- public void onConnect(Session session) {
- log.info("onConnectCounter={}", onConnectCounter.getAndIncrement());
- }
-
- @OnWebSocketMessage
- public void onMessage(Session session, String message) {
- log.info("onMessageCounter={}", onMessageCounter.getAndIncrement());
- log.debug("onMessage={}", message);
- }
-
- @OnWebSocketClose
- public void onClose(Session session, int statusCode, String reason) {
- try {
- } catch (Throwable e) {
- log.error(e, e);
- throw e;
- }
- }
-
- @OnWebSocketError
- public void onError(Session session, Throwable cause) {
- try {
- log.info("onError");
- log.error(cause, cause);
- } catch (Throwable e) {
- log.error(e, e);
- throw e;
- }
- }
- }
-
-}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/FirebaseUserSession.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/FirebaseUserSession.java
deleted file mode 100644
index e22bf81..0000000
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/fbauth/FirebaseUserSession.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.nkjmlab.go.javalin.fbauth;
-
-import javax.servlet.http.HttpSession;
-import org.nkjmlab.util.javax.servlet.UserSession;
-
-public class FirebaseUserSession extends UserSession {
-
- private static final String EMAIL = "email";
- private static final String ID_TOKEN = "idToken";
-
- public static FirebaseUserSession wrap(HttpSession session) {
- return new FirebaseUserSession(session);
- }
-
- protected FirebaseUserSession(HttpSession session) {
- super(session);
- }
-
-
- public String getEmail() {
- return getAttribute(EMAIL) == null ? null : getAttribute(EMAIL).toString();
- }
-
- public String getIdToken() {
- return getAttribute(ID_TOKEN) == null ? null : getAttribute(ID_TOKEN).toString();
- }
-
-
- public boolean isSetUserId() {
- return getUserId() != null;
- }
-
- public boolean isSigninFirebase() {
- return getIdToken() != null;
- }
-
- private void setEmail(String email) {
- setAttribute(EMAIL, email);
- }
-
- private void setIdToken(String idToken) {
- setAttribute(ID_TOKEN, idToken);
- }
-
- public void signinFirebase(String idToken, String email) {
- setIdToken(idToken);
- setEmail(email);
- setMaxInactiveInterval(10 * 60 * 60);
- }
-
-
-
-}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java
index c1687ca..06d1955 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java
@@ -1,135 +1,108 @@
package org.nkjmlab.go.javalin.jsonrpc;
-import static org.nkjmlab.go.javalin.GoApplication.*;
import java.io.File;
import java.time.LocalDateTime;
import java.util.Collections;
-import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.nkjmlab.go.javalin.GoApplication;
+import org.nkjmlab.go.javalin.GoTables;
+import org.nkjmlab.go.javalin.GoWebAppConfig;
import org.nkjmlab.go.javalin.model.common.ProblemJson;
-import org.nkjmlab.go.javalin.model.problem.ProblemTextToJsonConverter;
-import org.nkjmlab.go.javalin.model.relation.GameRecordsTable;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameStateJson;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTables;
-import org.nkjmlab.go.javalin.model.relation.HandUpsTable;
+import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState;
import org.nkjmlab.go.javalin.model.relation.HandUpsTable.HandUp;
-import org.nkjmlab.go.javalin.model.relation.LoginsTable;
-import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable;
import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable.MatchingRequest;
-import org.nkjmlab.go.javalin.model.relation.ProblemsTable;
import org.nkjmlab.go.javalin.model.relation.ProblemsTable.Problem;
-import org.nkjmlab.go.javalin.model.relation.UsersTable;
import org.nkjmlab.go.javalin.model.relation.UsersTable.User;
import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson;
-import org.nkjmlab.go.javalin.model.relation.VotesTable;
import org.nkjmlab.go.javalin.model.relation.VotesTable.Vote;
import org.nkjmlab.go.javalin.model.relation.VotesTable.VoteResult;
+import org.nkjmlab.go.javalin.util.CurrentTimeMillisIdGenerator;
import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager;
-import org.nkjmlab.go.javalin.websocket.WebsoketSessionsTable;
import org.nkjmlab.sorm4j.result.RowMap;
import org.nkjmlab.util.java.Base64Utils;
import org.nkjmlab.util.java.json.JsonMapper;
-import org.nkjmlab.util.java.lang.ParameterizedStringUtils;
+import org.nkjmlab.util.java.lang.ParameterizedStringFormatter;
import org.nkjmlab.util.javax.imageio.ImageIoUtils;
+import org.threeten.bp.Instant;
public class GoJsonRpcService implements GoJsonRpcServiceInterface {
private static final org.apache.logging.log4j.Logger log =
org.apache.logging.log4j.LogManager.getLogger();
+ private final WebsocketSessionsManager webSocketManager;
- private final GameStatesTables gameStatesTables;
- private final ProblemsTable problemsTable;
- private final UsersTable usersTable;
- private final LoginsTable loginsTable;
- private final MatchingRequestsTable matchingRequestsTable;
- private final VotesTable votesTable;
- private final WebsocketSessionsManager wsManager;
- private final WebsoketSessionsTable websoketSessionsTable;
- private final HandUpsTable handsUpTable;
- private final GameRecordsTable gameRecordsTable;
+ private final GoTables goTables;
private static final JsonMapper mapper = GoApplication.getDefaultJacksonMapper();
- public GoJsonRpcService(WebsocketSessionsManager wsManager, GameStatesTables gameStatesTables,
- ProblemsTable problemsTable, UsersTable usersTable, LoginsTable loginsTable,
- MatchingRequestsTable matchingRequestsTable, VotesTable votesTable, HandUpsTable handsUpTable,
- WebsoketSessionsTable websoketSessionsTable, GameRecordsTable gameRecordsTable) {
- this.gameStatesTables = gameStatesTables;
- this.problemsTable = problemsTable;
- this.usersTable = usersTable;
- this.loginsTable = loginsTable;
- this.matchingRequestsTable = matchingRequestsTable;
- this.wsManager = wsManager;
- this.votesTable = votesTable;
- this.handsUpTable = handsUpTable;
- this.websoketSessionsTable = websoketSessionsTable;
- this.gameRecordsTable = gameRecordsTable;
-
+ public GoJsonRpcService(WebsocketSessionsManager webSocketManager, GoTables goTables) {
+ this.webSocketManager = webSocketManager;
+ this.goTables = goTables;
}
@Override
- public void sendGameState(String gameId, GameStateJson json) {
- wsManager.sendGameState(gameId, json);
+ public void sendGameState(String gameId, GameState json) {
+ webSocketManager.sendGameState(gameId, json);
}
@Override
public void sendGlobalMessage(String userId, String message) {
- wsManager.sendGlobalMessage(message);
+ webSocketManager.sendGlobalMessage(message);
}
@Override
public void syncGameState(int sessionId, String gameId, String userId) {
- wsManager.updateSession(sessionId, gameId, userId);
- wsManager.sendLatestGameStateToSessions(gameId);
+ webSocketManager.updateSession(sessionId, gameId, userId);
+ webSocketManager.sendLatestGameStateToSessions(gameId);
}
@Override
- public void newGame(String gameId, GameStateJson json) {
- wsManager.newGame(gameId, json);
+ public void newGame(String gameId, GameState json) {
+ webSocketManager.newGame(gameId, json);
}
@Override
public ProblemJson loadProblem(String gameId, long problemId) {
- return wsManager.loadProblem(gameId, problemId);
+ return webSocketManager.loadProblem(gameId, problemId);
}
@Override
public ProblemJson getProblem(long problemId) {
- Problem p = problemsTable.selectByPrimaryKey(problemId);
+ Problem p = goTables.problemsTable.selectByPrimaryKey(problemId);
return p == null ? new ProblemJson(-1) : ProblemJson.createFrom(p);
}
@Override
- public void goBack(String gameId, GameStateJson json) {
- wsManager.goBack(gameId);
+ public void goBack(String gameId, GameState json) {
+ webSocketManager.goBack(gameId);
}
@Override
public ProblemJson saveProblem(String gameId, long problemId, String groupId, String name,
String message) {
Problem newP = createNewProblem(gameId, problemId, groupId, name, message);
- problemsTable.merge(newP);
- problemsTable.clearProblemsJson();
+ goTables.problemsTable.merge(newP);
+ goTables.problemsTable.clearProblemsJson();
ProblemJson problemJson = ProblemJson.createFrom(newP);
saveProblemJsonToFile(problemJson);
return problemJson;
}
+ private final CurrentTimeMillisIdGenerator problemIdGenerator = new CurrentTimeMillisIdGenerator();
+
private Problem createNewProblem(String gameId, long problemId, String groupId, String name,
String message) {
- Problem prevP = problemsTable.selectByPrimaryKey(problemId);
- GameStateJson currentState = gameStatesTables.readLatestGameStateJson(gameId);
+ Problem prevP = goTables.problemsTable.selectByPrimaryKey(problemId);
+ GameState currentState = goTables.gameStatesTables.readLatestGameState(gameId);
if (prevP != null) {
autoBackupProblemJsonToFile(ProblemJson.createFrom(prevP));
}
return new Problem(
- prevP != null ? prevP.id()
- : (problemId == -1 ? ProblemTextToJsonConverter.getNewId() : problemId),
+ prevP != null ? prevP.id() : (problemId == -1 ? problemIdGenerator.getNewId() : problemId),
LocalDateTime.now(), groupId, name, mapper.toJson(currentState.cells()),
mapper.toJson(currentState.symbols()), mapper.toJson(currentState.agehama()),
mapper.toJson(currentState.handHistory()), message == null ? "" : message);
@@ -137,7 +110,7 @@ private Problem createNewProblem(String gameId, long problemId, String groupId,
private void autoBackupProblemJsonToFile(ProblemJson p) {
File bkupDir = getProblemAutoBackupDir(p.groupId());
- File o = new File(bkupDir, new Date().getTime() + "-copy-" + p.name() + ".json");
+ File o = new File(bkupDir, Instant.now().toEpochMilli() + "-copy-" + p.name() + ".json");
mapper.toJsonAndWrite(p, o, true);
}
@@ -145,18 +118,20 @@ private void saveProblemJsonToFile(ProblemJson p) {
File problemGroupDir = getProblemDir(p.groupId());
File o = new File(problemGroupDir, p.name() + ".json");
mapper.toJsonAndWrite(p, o, true);
- log.info("Problep {} - {} is saved to {}", p.groupId(), p.name(), o);
+ log.info("Problem {} - {} is saved to {}", p.groupId(), p.name(), o);
}
private File getProblemDir(String groupId) {
- File dir = new File(PROBLEM_DIR, groupId);
+ File dir = new File(GoWebAppConfig.PROBLEM_DIR, groupId);
dir.mkdirs();
return dir;
}
private File getProblemAutoBackupDir(String groupId) {
- File dir = new File(PROBLEM_BACKUP_DIR, groupId);
+ File dir =
+ new File(new File(GoWebAppConfig.WEB_APP_CONFIG.getAppRootDirectory(), "problem-auto-bkup"),
+ groupId);
dir.mkdirs();
return dir;
@@ -164,19 +139,19 @@ private File getProblemAutoBackupDir(String groupId) {
@Override
public void deleteProblem(long problemId) {
- Problem p = problemsTable.selectByPrimaryKey(problemId);
+ Problem p = goTables.problemsTable.selectByPrimaryKey(problemId);
if (p == null) {
return;
}
autoBackupProblemJsonToFile(ProblemJson.createFrom(p));
- problemsTable.delete(p);
- problemsTable.clearProblemsJson();
+ goTables.problemsTable.delete(p);
+ goTables.problemsTable.clearProblemsJson();
}
@Override
public ProblemJson readProblem(long problemId) {
- Problem p = problemsTable.selectByPrimaryKey(problemId);
+ Problem p = goTables.problemsTable.selectByPrimaryKey(problemId);
if (p != null) {
return ProblemJson.createFrom(p);
}
@@ -186,39 +161,39 @@ public ProblemJson readProblem(long problemId) {
@Override
public String getNextUser(String currentGameId) {
- String nextUserId = loginsTable.getNextLoginUserId(usersTable, currentGameId);
+ String nextUserId = goTables.loginsTable.getNextLoginUserId(goTables.usersTable, currentGameId);
return nextUserId;
}
@Override
public String getPrevUser(String currentGameId) {
- String nextUserId = loginsTable.getPrevLoginUserId(usersTable, currentGameId);
+ String nextUserId = goTables.loginsTable.getPrevLoginUserId(goTables.usersTable, currentGameId);
return nextUserId;
}
@Override
public String getNextQuestion(String currentGameId) {
- String nextQuestionGameId = handsUpTable.getNextQuestion(currentGameId);
+ String nextQuestionGameId = goTables.handsUpTable.getNextQuestion(currentGameId);
return nextQuestionGameId;
}
@Override
public UserJson getUser(String userId) {
- User u = usersTable.selectByPrimaryKey(userId);
+ User u = goTables.usersTable.selectByPrimaryKey(userId);
if (u == null) {
UserJson uj = new UserJson(userId);
return uj;
}
- UserJson uj = new UserJson(u, loginsTable.isAttendance(userId));
+ UserJson uj = new UserJson(u, goTables.loginsTable.isAttendance(userId));
return uj;
}
@Override
public String getNextGame(String currentGameId) {
- List ids = websoketSessionsTable.readActiveGameIdsOrderByGameId(usersTable);
+ List ids = webSocketManager.readActiveGameIdsOrderByGameId();
return getNextId(ids, currentGameId);
}
@@ -235,7 +210,7 @@ private String getNextId(List ids, String currentGameId) {
@Override
public String getPrevGame(String currentGameId) {
- List ids = websoketSessionsTable.readActiveGameIdsOrderByGameId(usersTable);
+ List ids = webSocketManager.readActiveGameIdsOrderByGameId();
Collections.reverse(ids);
return getNextId(ids, currentGameId);
}
@@ -244,21 +219,21 @@ public String getPrevGame(String currentGameId) {
@Override
public void enterWaitingRoom(String userId) {
try {
- User u = usersTable.selectByPrimaryKey(userId);
+ User u = goTables.usersTable.selectByPrimaryKey(userId);
if (u == null) {
log.error("userId {} is not found.", userId);
return;
}
- usersTable.update(u);
+ goTables.usersTable.update(u);
MatchingRequest matchingReq = MatchingRequest.createUnpaired(u);
- if (!matchingRequestsTable.exists(matchingReq)) {
- matchingRequestsTable.insert(matchingReq);
+ if (!goTables.matchingRequestsTable.exists(matchingReq)) {
+ goTables.matchingRequestsTable.insert(matchingReq);
} else {
- MatchingRequest m = matchingRequestsTable.selectByPrimaryKey(userId);
- matchingRequestsTable.update(m);
+ MatchingRequest m = goTables.matchingRequestsTable.selectByPrimaryKey(userId);
+ goTables.matchingRequestsTable.update(m);
}
- wsManager.sendUpdateWaitingRequestStatus(Set.of(userId));
+ webSocketManager.sendUpdateWaitingRequestStatus(Set.of(userId));
} catch (Exception e) {
log.error("maching request for {} failed", userId);
log.error(e, e);
@@ -268,23 +243,22 @@ public void enterWaitingRoom(String userId) {
@Override
public void exitWaitingRoom(String userId) {
- // logger.debug("{} exited from waiting room.", userId);
- matchingRequestsTable.deleteByPrimaryKey(userId);
- wsManager.sendUpdateWaitingRequestStatus(Set.of(userId));
+ goTables.matchingRequestsTable.deleteByPrimaryKey(userId);
+ webSocketManager.sendUpdateWaitingRequestStatus(Set.of(userId));
}
@Override
public File uploadImage(String userId, String base64EncodedImage) {
try {
{
- File outputFile = new File(UPLOADED_ICON_DIR, userId + ".png");
+ File outputFile = new File(GoWebAppConfig.UPLOADED_ICON_DIR, userId + ".png");
outputFile.mkdirs();
ImageIoUtils.write(Base64Utils.decodeToImage(base64EncodedImage, "png"), "png", outputFile);
}
- File outputFile = new File(CURRENT_ICON_DIR, userId + ".png");
+ File outputFile = new File(GoWebAppConfig.CURRENT_ICON_DIR, userId + ".png");
outputFile.mkdirs();
ImageIoUtils.write(Base64Utils.decodeToImage(base64EncodedImage, "png"), "png", outputFile);
- log.info("Icon is uploaded={}", outputFile);
+ log.debug("Icon is uploaded={}", outputFile);
return outputFile;
} catch (Exception e) {
log.error(e, e);
@@ -300,12 +274,13 @@ public boolean sendLog(String logLevel, String location, String msg, String opti
@Override
public void vote(String gameId, String userId, long problemId, String vote, String voteId) {
- votesTable.merge(new Vote(userId, problemId, vote, voteId, gameId, LocalDateTime.now()));
+ goTables.votesTable
+ .merge(new Vote(userId, problemId, vote, voteId, gameId, LocalDateTime.now()));
}
@Override
public List getVoteResult(long problemId, String gameId) {
- List ret = votesTable.readVoteResults(problemId, gameId);
+ List ret = goTables.votesTable.readVoteResults(problemId, gameId);
return ret;
}
@@ -313,34 +288,33 @@ public List getVoteResult(long problemId, String gameId) {
public void handUp(String gameId, boolean handUp, String message) {
if (handUp) {
{
- HandUp h = handsUpTable.selectByPrimaryKey(gameId);
+ HandUp h = goTables.handsUpTable.selectByPrimaryKey(gameId);
if (h == null) {
- handsUpTable.insert(new HandUp(gameId, LocalDateTime.now(), message));
+ goTables.handsUpTable.insert(new HandUp(gameId, LocalDateTime.now(), message));
} else {
- handsUpTable
+ goTables.handsUpTable
.update(new HandUp(h.gameId(), h.createdAt(), h.message() + "
" + message));
}
}
- wsManager.sendHandUp(gameId, handUp, handsUpTable.readOrder(gameId));
+ webSocketManager.sendHandUp(gameId, handUp, goTables.handsUpTable.readOrder(gameId));
} else {
- handsUpTable.deleteByPrimaryKey(gameId);
- wsManager.sendHandDown(gameId);
+ goTables.handsUpTable.deleteByPrimaryKey(gameId);
+ webSocketManager.sendHandDown(gameId);
- handsUpTable.readAllGameIds().stream().forEach(handupGameId -> wsManager
- .sendHandUpOrder(handupGameId, handsUpTable.readOrder(handupGameId)));
+ goTables.handsUpTable.readAllGameIds().stream().forEach(handupGameId -> webSocketManager
+ .sendHandUpOrder(handupGameId, goTables.handsUpTable.readOrder(handupGameId)));
}
}
@Override
public int registerRecord(String userId, String opponentUserId, String jadge, String memo) {
- // logger.debug("{},{},{},{}", userId, opponentUserId, jadge, memo);
- int rank =
- gameRecordsTable.registerRecordAndGetRank(usersTable, userId, opponentUserId, jadge, memo);
+ int rank = goTables.gameRecordsTable.registerRecordAndGetRank(goTables.usersTable, userId,
+ opponentUserId, jadge, memo);
- User u = usersTable.selectByPrimaryKey(userId);
+ User u = goTables.usersTable.selectByPrimaryKey(userId);
if (u.rank() != rank) {
- usersTable.updateByPrimaryKey(RowMap.of("rank", rank), u.userId());
+ goTables.usersTable.updateByPrimaryKey(RowMap.of("rank", rank), u.userId());
return rank;
}
return -1;
@@ -358,27 +332,27 @@ public int registerRecord(String userId, String opponentUserId, String jadge, St
@Override
public String getKomi(String gameId) {
- String msg = "";
try {
- GameStateJson gs = gameStatesTables.readLatestGameStateJson(gameId);
- User bp = usersTable.selectByPrimaryKey(gs.blackPlayerId());
- User wp = usersTable.selectByPrimaryKey(gs.whitePlayerId());
+ GameState gs = goTables.gameStatesTables.readLatestGameState(gameId);
+ User bp = goTables.usersTable.selectByPrimaryKey(gs.blackPlayerId());
+ User wp = goTables.usersTable.selectByPrimaryKey(gs.whitePlayerId());
int diff = Math.abs(wp.rank() - bp.rank());
int ro = gs.cells()[0].length;
String roCol = ro == 19 ? "lg" : "sm";
String s1 = start.get(roCol).get(Math.min(diff, 5));
String s2 = midFlow.get(roCol).get(Math.min(diff, 5));
+ Object[] params = {bp.userId(), bp.userName(), bp.rank(), wp.userId(), wp.userName(),
+ wp.rank(), diff, ro, s1, s2};
- msg = ParameterizedStringUtils.newString(
- "{} ({},{}級) vs {} ({},{}級): {}級差,{}路
はじめから {}, 棋譜並べから {}
",
- bp.userId(), bp.userName(), bp.rank(), wp.userId(), wp.userName(), wp.rank(), diff, ro,
- s1, s2);
+ String msg = ParameterizedStringFormatter.DEFAULT.format(
+ (String) "{} ({},{}級) vs {} ({},{}級): {}級差,{}路
はじめから {}, 棋譜並べから {}
",
+ params);
+ return msg;
} catch (Exception e) {
- msg = "";
log.error(e);
+ return "";
}
- return msg;
}
}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcServiceInterface.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcServiceInterface.java
index d494d05..00c4f11 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcServiceInterface.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcServiceInterface.java
@@ -3,7 +3,7 @@
import java.io.File;
import java.util.List;
import org.nkjmlab.go.javalin.model.common.ProblemJson;
-import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameStateJson;
+import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState;
import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson;
import org.nkjmlab.go.javalin.model.relation.VotesTable.VoteResult;
@@ -30,15 +30,15 @@ public interface GoJsonRpcServiceInterface {
boolean sendLog(String logLevel, String location, String msg, String options);
- void newGame(String gameId, GameStateJson json);
+ void newGame(String gameId, GameState json);
ProblemJson loadProblem(String gameId, long problemId);
ProblemJson getProblem(long problemId);
- void goBack(String gameId, GameStateJson json);
+ void goBack(String gameId, GameState json);
- void sendGameState(String gameId, GameStateJson json);
+ void sendGameState(String gameId, GameState json);
ProblemJson saveProblem(String gameId, long problemId, String groupId, String name,
String message);
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Agehama.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Agehama.java
index eb09b68..bb9c8d6 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Agehama.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Agehama.java
@@ -1,5 +1,8 @@
package org.nkjmlab.go.javalin.model.common;
+import org.nkjmlab.sorm4j.util.datatype.OrmJsonColumnContainer;
+
+@OrmJsonColumnContainer
public record Agehama(int black, int white) {
public Agehama increment(Stone stone) {
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Hand.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Hand.java
index 68bb491..80c46c0 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Hand.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/common/Hand.java
@@ -1,6 +1,7 @@
package org.nkjmlab.go.javalin.model.common;
import java.util.stream.Stream;
+import org.nkjmlab.sorm4j.util.datatype.OrmJsonColumnContainer;
/**
* stone
@@ -8,6 +9,7 @@
* 2桁目 0:ブランク, 1:□, 2:△, 3:x
*
*/
+@OrmJsonColumnContainer
public record Hand(String type, int number, int x, int y, int stone, String options) {
public static Hand createDummyHand() {
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/problem/ProblemJsonReader.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/problem/ProblemJsonReader.java
deleted file mode 100644
index bf8d685..0000000
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/problem/ProblemJsonReader.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.nkjmlab.go.javalin.model.problem;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.nkjmlab.go.javalin.GoApplication;
-import org.nkjmlab.go.javalin.model.common.ProblemJson;
-
-public class ProblemJsonReader {
- private static final org.apache.logging.log4j.Logger log =
- org.apache.logging.log4j.LogManager.getLogger();
-
-
- private static List readProblemJsonFiles(Path pathToProblemJsonDir) {
- List result = new ArrayList<>();
- getGroupDirectories(pathToProblemJsonDir).forEach(groupDir -> {
- Arrays.asList(groupDir.listFiles()).forEach(file -> {
- if (!file.getName().endsWith(".json")) {
- return;
- }
- result.add(file);
- });
- });
- return result;
- }
-
- public static List readProblemJsons(Path pathToProblemJsonDir) {
- List files = readProblemJsonFiles(pathToProblemJsonDir);
- log.info("detect [{}] problem files in [{}]", files.size(), pathToProblemJsonDir);
- return files.stream().map(file -> {
- try {
- ProblemJson problem =
- GoApplication.getDefaultJacksonMapper().toObject(file, ProblemJson.class);
- return problem;
- } catch (Exception e) {
- log.error("file {}", file);
- throw new RuntimeException(e);
- }
- }).collect(Collectors.toList());
- }
-
- private static List getGroupDirectories(Path path) {
- File[] files = path.toFile().listFiles();
- if (files != null) {
- return Arrays.asList(files).stream().filter(f -> f.isDirectory())
- .collect(Collectors.toList());
- }
- return new ArrayList<>();
- }
-
-}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameRecordsTable.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameRecordsTable.java
index 7b417db..1b209ec 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameRecordsTable.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameRecordsTable.java
@@ -1,6 +1,5 @@
package org.nkjmlab.go.javalin.model.relation;
-import static org.nkjmlab.go.javalin.model.relation.GameRecordsTable.GameRecord.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@@ -15,6 +14,9 @@
import org.nkjmlab.sorm4j.util.table_def.annotation.PrimaryKey;
public class GameRecordsTable extends BasicH2Table {
+ private static final String CREATED_AT = "created_at";
+ private static final String USER_ID = "user_id";
+ private static final String RANK = "rank";
public GameRecordsTable(DataSource dataSource) {
super(Sorm.create(dataSource), GameRecord.class);
@@ -92,10 +94,6 @@ public List readByUserId(String userId) {
public static record GameRecord(@PrimaryKey @AutoIncrement int id, LocalDateTime createdAt,
String userId, String opponentUserId, String jadge, String memo, int rank, int point,
String message) {
- private static final String CREATED_AT = "created_at";
- private static final String USER_ID = "user_id";
- private static final String RANK = "rank";
-
}
diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameStatesTable.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameStatesTable.java
index 56e7d55..d1a9b5c 100644
--- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameStatesTable.java
+++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GameStatesTable.java
@@ -18,13 +18,12 @@
import org.nkjmlab.sorm4j.Sorm;
import org.nkjmlab.sorm4j.annotation.OrmRecord;
import org.nkjmlab.sorm4j.util.h2.BasicH2Table;
+import org.nkjmlab.sorm4j.util.jackson.JacksonSormContext;
import org.nkjmlab.sorm4j.util.table_def.annotation.AutoIncrement;
import org.nkjmlab.sorm4j.util.table_def.annotation.Index;
import org.nkjmlab.sorm4j.util.table_def.annotation.IndexColumns;
import org.nkjmlab.sorm4j.util.table_def.annotation.NotNull;
import org.nkjmlab.sorm4j.util.table_def.annotation.PrimaryKey;
-import org.nkjmlab.util.jackson.JacksonMapper;
-import com.fasterxml.jackson.core.type.TypeReference;
public class GameStatesTable extends BasicH2Table {
private static final org.apache.logging.log4j.Logger log =
@@ -45,7 +44,10 @@ public class GameStatesTable extends BasicH2Table {
public GameStatesTable(DataSource dataSource) {
- super(Sorm.create(dataSource), GameState.class);
+ super(
+ Sorm.create(dataSource, JacksonSormContext
+ .builder(GoApplication.getDefaultJacksonMapper().getObjectMapper()).build()),
+ GameState.class);
}
@@ -82,7 +84,7 @@ public void trimAndBackupToFile(File backUpDir, int limit) {
String selectSql =
selectStarFrom(getTableName()) + where(cond(ROWNUM, "<=", deleteRowNum)) + orderBy(ID);
- String st = getCallCsvWriteSql(outputFile, selectSql, StandardCharsets.UTF_8, ',');
+ String st = getCallCsvWriteSql(outputFile, selectSql, StandardCharsets.UTF_8, ',', null);
log.info("{}", st);
getOrm().executeUpdate(st);
@@ -107,46 +109,19 @@ public List readTodayGameIds() {
@IndexColumns({BLACK_PLAYER_ID, WHITE_PLAYER_ID})
public record GameState(@PrimaryKey @AutoIncrement long id, LocalDateTime createdAt,
@Index @NotNull String gameId, @NotNull String blackPlayerId, @NotNull String whitePlayerId,
- @NotNull String lastHand, @NotNull String agehama, @NotNull String cells,
- @NotNull String symbols, @NotNull String handHistory, @NotNull long problemId,
- @NotNull String options) {
- }
-
- public record GameStateJson(long id, String gameId, String blackPlayerId, String whitePlayerId,
- int[][] cells, Map symbols, Agehama agehama, Hand lastHand,
- Hand[] handHistory, long problemId, Map options, LocalDateTime createdAt) {
+ @NotNull Hand lastHand, @NotNull Agehama agehama, @NotNull Integer[][] cells,
+ @NotNull Map symbols, @NotNull Hand[] handHistory, @NotNull long problemId,
+ @NotNull Map options) {
public static final String DEFAULT_PLAYER_ID = "-1";
public static final int DEFAULT_RO = 9;
- private static final JacksonMapper mapper = GoApplication.getDefaultJacksonMapper();
-
- public GameStateJson updateHandHistory(List modifiedHistory) {
- return new GameStateJson(id, gameId, blackPlayerId, whitePlayerId, cells, symbols, agehama,
- lastHand, modifiedHistory.toArray(Hand[]::new), problemId, options, createdAt);
- }
-
-
- public GameStateJson(GameState gameState) {
- this(gameState.id(), gameState.gameId(), gameState.blackPlayerId(), gameState.whitePlayerId(),
- mapper.toObject(gameState.cells(), int[][].class),
- mapper.toObject(gameState.symbols(), new TypeReference