LoginSignup
0

posted at

updated at

【kabuswebapp】auカブコム証券のkabuステーションREST APIをJava Servletで叩く

はじめに

過去記事は「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ソース

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0