はじめに
過去記事は「auカブコム証券のkabuステーションREST APIに関する記事一覧」。
2番目の記事「auカブコム証券のkabuステーションREST APIをjava(generated by the swagger code generator)で叩く」では、Swaggerを使ってRESTクライアントをソース生成して、javaのコンソールアプリを作成した。
毎回プロセス起動するため、認証トークンをファイルに保存して、ファイルロックで排他制御したりしたが、Webアプリならば、常駐しているので、javaならばsynchronizedでスレッド同期できることから、以前と同じように、単体のREST APIを呼び出すServletページを作ってみる。
swagger editorとその他APIヘルパー
なかなかの手間なので、コンソールアプリ(kabusapp)で使っているジェネレートされたソースとJUnitテストを持ってきた。
また、ジェネレートされたソースをラップしたapiと列挙体やutilをコピーして、コンソールアプリとほぼ同じように使えるようにした。
ファイルが競合するとデバッグ時に混乱するので、一部ファイルパスをサーバー用に変える。
ただし、ログインパスワードファイルなどが2ヵ所にあるのも混乱するので、共通な設定はコンソールアプリのファイルを使う。
private static final String DIRPATH = "/tmp/";
private static final String SERVER_DIRPATH = "/tmp/server/";
認証API
ログイン(/login) Servletを作成する。
実際にWebアプリを作って、ログインエラーなどが発生すると問題があったので、「【logic,util】ファイルロックに関する見直し」の対応済のソースを使い、try-with-resourcesで記述する。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try (LockedAuthorizedTokenLogic login = new LockedAuthorizedTokenLogic()) {
String X_API_KEY = login.getApiKey();
response.getWriter().append("X_API_KEY=").append(X_API_KEY);
} catch (ApiException e) {
e.printStackTrace();
response.getWriter().append("Error: ").append(e.getResponseBody());
}
}
ApiErrorLog初期化
コンソールアプリ(kabusapp)のSwaggerの生成したソースをラップしたapi.*がApiErrorLogクラスを呼んで、エラー時のログファイルを出力するため、ApiErrorLogクラスの初期化をVersionServletで行う。
ただし、先にLoginServletにアクセスすると初期化されないため、web.xmlに<load-on-startup>を追加し、アプリサーバー起動時にVersionServletがロードされるようにする。
public VersionServlet() {
super();
ApiErrorLog.init(MethodHandles.lookup().lookupClass(), Consts.VERSION);
}
<servlet>
<servlet-name>VersionServlet</servlet-name>
<display-name>VersionServlet</display-name>
<description></description>
<servlet-class>server.web.VersionServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
実行結果
Webブラウザで /login にアクセスして、成功すれば認証済トークンが表示される。
X_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
時価情報・板情報API
コンソールアプリのときと同じく、時価情報を取得して、表示してみる。
WebアプリならばHTTPパラメータで銘柄コードでも渡せばいいが、とりあえずアプリを作りたいわけではないので、定数で埋め込んだ銘柄のBoardSuccessを表示する。
このときに日本語が文字化けするので、setContentTypeを指定する(ログインも同様)。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
try (LockedAuthorizedTokenLogic login = new LockedAuthorizedTokenLogic()) {
String X_API_KEY = login.getApiKey();
String symbol = "9433@1"; // KDDI
InfoApi infoApi = new InfoApi();
BoardSuccess board = infoApi.boardGet(X_API_KEY, symbol);
System.out.println(board);
response.getWriter().append(board.toString());
問題点
現時点で気が付いた問題点として、Tomcatにダイナミックにデプロイを繰り返すと、CPU負荷が上がってくる。
ログを見ると、スレッドが停止できないエラーが見つかる。
21-Oct-2022 13:11:56.125 警告 [Catalina-utility-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads Webアプリケーション [kabuswebapp] は [Okio Watchdog] という名前のスレッドを開始したようですが、停止に失敗しました。これはメモリリークを引き起こす可能性が非常に高いです。スレッドのスタックトレース:
java.base@11.0.14.1/java.lang.Object.wait(Native Method)
java.base@11.0.14.1/java.lang.Object.wait(Object.java:328)
okio.AsyncTimeout.awaitTimeout(AsyncTimeout.java:311)
okio.AsyncTimeout.access$000(AsyncTimeout.java:40)
okio.AsyncTimeout$Watchdog.run(AsyncTimeout.java:286)
jconsoleでスレッドを見たら、OkHttpが2つスレッドを生成しているようだ。
- OkHttp ConnectionPool
- Okio Watchdog
gitポーリングしてビルドごとにデプロイするのは危なそうなので、毎回Tomcatを再起動することにする。
追記:銘柄コード取得APIラッパー
InfoApiWrapperに切り替えたToolSymbolNameを使うSymbolNameServletを作成する。
HTTPレスポンスは、execute()の中でSystem.out.printlnをListに貯えて、クライアントに返す。
githubソース