20210913のJavaに関する記事は5件です。

転職活動管理アプリを作ろう③(ログイン実装チュートリアル~テーブル定義)

前回は コチラ ※ 2021/09/13 時点で未完 ログインを実装してみよう 以下チュートリアルを行う 上記チュートリアルだとインメモリにアカウントレコードを登録し、そのレコードに対し認証を行う MySQL を使用するように設定変更を行う 設定変更をして動作確認ができるように、テーブル定義だけ先に行うことにした テーブル定義 各テーブルについて所管を記述する 実際のテーブル定義書については後ほど公開するかも (少しずつ追記していきます) アカウント情報 テーブル名は「Account」 spring-boot-starter-security が提供してくれるアカウント情報テーブル(= User テーブル)の初期スキーマは以下 username:String ユーザー名称。DaoAuthenticationProvider に渡されて認証が行われる password:String パスワード。DaoAuthenticationProvider に渡されて認証が行われる enabled:boolean 論理削除フラグ。 accountNonExpired:boolean アカウントの有効性フラグ。 credentialsNonExpired:boolean 認証情報(通常はパスワード)の有効性フラグ。 最近の考え方だとパスワードの頻繁な変更は脆弱とのことなので、今はあまり使わないかな? パスワードを忘れた時の対処とかに流用できそう。 accountNonLocked:boolean アカウントロックフラグ。 authorities:Collection<? extends GrantedAuthority> 役割。 一旦 String 配列で持ったあと、createAuthorityList() で GrantedAuthority の List 化 チュートリアル中だと、WebSecurityConfig#userDetailsService() の User.role("USER") で実行される 上記初期スキーマの内容で十分なので、このままテーブルを作成する 作成日、最終更新日ぐらいは追加しておく ちなみに org.springframework.security.core.userdetails.User エンティティクラスを提供してくれているので、使い方は Javadoc を参照 実装する際には一応継承して自作クラスを使用しようね 役割 DB にテーブルは作らない enum 型にして静的に持つことにした 取り得る値は以下 ROLE_USER 一般ユーザー ROLE_ADMIN 管理者 今の所、各役割で動作の振る舞いは変わらない。気が向いたらどこかの画面の実装で出し分けしてみる 役割テーブルを作って DB に格納 → アプリケーション起動時にメモリに格納 → 静的に運用、も考えたけど保持する情報が文字列だけなので不要と判断 各役割ごとに実施できる機能も DB で管理するならアリ エージェント テーブル名は「Agent」 スキーマは以下を想定 id:sequence PK。 name:String エージェント名称(会社名) top_url:String トップページURL login_url:String ログインURL エージェント担当者 テーブル名は「AgentRecruiter」 スキーマは以下を想定 id:sequence PK。 agent_id:int Agent テーブルの ID(外部キー) name:String 担当者名 email:String メールアドレス 企業 テーブル名は「Company」 スキーマは以下を想定 id:sequence PK。 name:String 企業名称 corporate_url:String コーポレートサイトの URL recruit_url:String 採用サイトの URL 企業の求人票 テーブル名は「CompanyJobPosting」 企業のマークダウン形式メモ テーブル名は「CompanyMarkdown」 企業の評価ポイント テーブル名は「CompanyEvaluationStandard」 選考ステップ DB にテーブルは作らない enum 型にして静的に持つことにした 取り得る値は以下 IN_BOX 候補 SCREENING 書類選考 PRIMARY 一次選考 SECONDARY 二次選考 FINAL 最終選考 選考ステップの中に後述の「選考ステータス」を持つ 選考ステータス DB にテーブルは作らない enum 型にして静的に持つことにした 取り得る値は以下 NEW 新規作成 IN_PLANNING 計画中・準備中 NOT_READY 実施予定なし WAITING 待機中 READY 準備完了 IN_PROGRESS 実施中 PENDING 保留中 REJECTED 拒否 CANCELED キャンセル COMPLETED 完了 メモ エンティティから DDL を自動生成し実行してくれるオプション spring.jpa.hibernate.ddl-auto=*** https://qiita.com/KenjiOtsuka/items/8450c407ba121fea8151 エンティティから DDL を自動生成する だけ のプラグイン JPA Schema Generator Plugin https://tosi-tech.net/2019/01/generate-mysql-ddl-from-jpa-entity-class/ https://github.com/Devskiller/jpa2ddl エンティティで使用するアノテーション一覧 https://qiita.com/ughirose/items/5d691adc677aa08636b8 DB のスキーマを HTML で出力 https://qiita.com/nabedge/items/e9325cf214a78e378aee Draw.io で ER 図を描ける https://qiita.com/Ooooooomin_365/items/3118c0c5fab4e100dd4f
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[paiza]スキルチェック見本問題 Java Fizz Buzz (paizaランク C 相当)

初めに 今回スキルアップチェック見本問題CのFizz Buzzに挑戦しました。 見本問題のため、問題の掲載、解答の掲載は自由です。 整数 N が入力として与えられます。 1からNまでの整数を1から順に表示してください。 ただし、表示しようとしている数値が、 ・3の倍数かつ5の倍数のときには、"Fizz Buzz" ・3の倍数のときには、"Fizz" ・5の倍数のときには、"Buzz" を数値の代わりに表示してください。 解答コード import java.util.*; public class Main { public static void main(String[] args) { // 自分の得意な言語で // Let's チャレンジ!! Scanner sc = new Scanner(System.in); int count = sc.nextInt(); for(int i=1; i<=count; i++){ if(i%15==0){ System.out.println("Fizz Buzz"); }else if(i%3==0){ System.out.println("Fizz"); }else if(i%5==0){ System.out.println("Buzz"); }else{ System.out.println(i); } } } } "Fizz Buzz" の間のスペースを忘れ一度不正解になってしまいましたが、 なんとか正解できました。 paiza側の解答もほぼ同じような感じでした。 参考サイト Rubyでのfizz buzz
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot ActuatorをKubernetesのProbeとして使う

やりたいこと Spring Bootアプリをコンテナ化してKubernetes上で動かす際、Spring Boot ActuatorをKubernetesのProbeで指定したい。 確認した環境は次のとおりです。 Spring Boot 2.4.10および2.5.4 AdoptOpenJDK 16.0.1 macOS Big Sur 11.5.2 Kubernetes 1.18.0 on Minikube 1.23.0 そもそもProbeって何? Probeとは、KubernetesがPod内のコンテナを監視するための機能です。 Probeには下記の3種類があります。 名前 説明 Probeに失敗した場合の処理 Startup Probe コンテナの起動処理が完了したかどうかを判定する。Startup Probe成功が確認できた後、以降のProbeが実行される。 コンテナが再起動される Liveness Probe コンテナが再起動が必要かを判定する。 コンテナが再起動される Readinesss Probe コンテナがServiceによる負荷分散対象にできるかを判定する。 Serviceによる負荷分散対象から外される(コンテナの再起動はされない) 具体的なProbe実行方法としては次の3つです。 No. 名前 説明 1 httpGet コンテナの特定のポート番号・パスにHTTP GETリクエストを送信して、レスポンスのステータスコードが200以上400未満であれば成功、そうでなければ失敗 2 tcpSocket コンテナの特定のポート番号とTCPコネクションを確立できれば成功、そうでなければ失敗 3 exec コンテナ内で特定のコマンドを実行して、0が返ってくれば成功、そうでなければ失敗 Spring Bootで作るようなWebアプリケーションの場合は、1を使うことが多いでしょう。 Spring Boot ActuatorのProbe対応 Spring Boot Actuatorでは、バージョン2.3でProbe対応しました(リリースノート)。 具体的には、下記のようなActuatorエンドポイントが追加されました。 エンドポイント 対応するProbe /actuator/health/liveness Liveness Probe /actuator/health/readiness Readiness Probe Startup Probeに対応するエンドポイントは用意されていません。Spring Boot Referenceには次のようにあります。 The "startupProbe" is not necessarily needed here as the "readinessProbe" fails until all startup tasks are done 要は「Readiness Probeで代用できるよね?」ってことですかね? Actuator利用手順 (1)依存性の追加 pom.xmlにspring-boot-starter-actuatorを含めます。 pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> ... </dependencies> (2)application.propertiesの設定 ProbeのActuatorエンドポイントを有効化するには、2つの方法があります(どちらか一方のみでOK)。 application.propertiesに management.endpoint.health.probes.enabled=true を設定する Kubernetes上で動かす(環境変数 *_SERVICE_HOST および *_SERVICE_PORT があるかをチェックしている ようです) Kubernetes上で動いているときに management.endpoint.health.probes.enabled=true があっても害はないので、基本的には1.の方法で良いでしょう。 あとは、healthエンドポイントを公開すればOKです。 以上をまとめると、application.propertiesを次のように書けばOKです。 application.properties management.endpoint.health.probes.enabled=true management.endpoints.web.exposure.include=health management.health.livenessstate.enabled と management.health.readinessstate.enabled という設定もあるのですが、これの意味がちょっと分かっていません・・・後ほど再調査予定。 Kubernetesマニフェストへの記述 path フィールドにActuatorのパスを指定します。 マニフェストファイル apiVersion: apps/v1 kind: Deployment metadata: name: hoge labels: app: hoge spec: replicas: 1 selector: matchLabels: app: hoge template: metadata: labels: app: hoge spec: containers: - name: hoge image: hoge:0.0.1 ports: - containerPort: 8080 ... livenessProbe: initialDelaySeconds: 10 httpGet: port: 8080 path: /actuator/health/liveness periodSeconds: 5 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 1 readinessProbe: initialDelaySeconds: 10 httpGet: port: 8080 path: /actuator/health/readiness periodSeconds: 5 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 2 ... 参考資料 Spring Boot Reference 2.9. Kubernetes Probes
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Couchbase Server Java SDK解説:エラー処理の応用

はじめに ここでは、下記の記事の続編として、Couchbase Server Java SDKにおける、例外処理の応用を整理します。 エラー処理の応用 RetryStrategyとRetryReasons RetryStrategyは、RetryReasonに基づいて、リクエストを再試行するかどうかを決定します。デフォルトでは、SDKにはBestEffortRetryStrategyが付属しており、再試行可能なエラーが発生すると、成功するかタイムアウトが期限切れになるまでリクエストを再試行します。 SDK 2のには、アプリケーションでの使用を目的としたFailFastRetryStrategyが付属しています。 SDK 3にも同梱されていますが、@Internalとしてマークされています。 RetryStrategyのカスタマイズの説明に従って、BestEffortRetryStrategyを拡張およびカスタマイズすることをお勧めします。 RetryReasons(参照を見てもいいです参考彼らは操作を再試行しまった理由についての洞察を与えているため、セクションを)。ErrorContext要求があるためさまざまな理由で複数回再試行されることを確実に可能であるため、前の章で説明したが、リストなどの理由を公開します。そのため、ディスパッチ中にソケットがダウンしたために要求が再試行された場合と、応答が一時的な障害を示したために再試行された場合があります。 デフォルトの動作をニーズに合わせて調整する方法の詳細については、RetryStrategyのカスタマイズを参照してください。 例外処理 Javaでは、すべての例外はCouchbaseExceptionベースクラスから派生します。 これは、グループ化メカニズムとしても、必要な場合の「すべてをキャッチ」する可能性としても機能します。ErrorContextはcontext()ゲッターを定義していますが、一部の例外ではnullになる場合があります。利用可能な場合は、上記のように例外ログ出力に自動的に含まれます。CouchbaseExceptionはRuntimeExceptionを拡張しており、SDKにはチェック例外(checked exception)は定義されていません。 SDKが透過的に再試行可能なすべての例外をすでに再試行している場合(RetryStrategyを調整しない限り)、再試行できない端末例外、またはSDKが独自に決定するのに十分なコンテキストがない場合にのみ残されます。 ブロッキングAPIでの例外の処理 さまざまtry/catchな戦略を説明するために、単純な例であるKey/Valueを介したドキュメントのロードについて考えてみましょう。 まず、ドキュメントが存在しないと予想しない場合は、DocumentNotFoundExceptionを致命的なエラーとして扱う可能性があります。この場合CouchbaseException、コールスタックを上に伝播するか、カスタム例外を使用して再スローすることができます(ここでは任意にDatabaseExceptionを定義しています)。 // This will raise a `CouchbaseException` and propagate it GetResult result = collection.get("my-document-id"); // Rethrow with a custom exception type try { collection.get("my-document-id"); } catch (CouchbaseException ex) { throw new DatabaseException("Couchbase lookup failed", ex); } 別のケースとして、ドキュメントが存在しない場合は、ドキュメントを作成する必要があることを示している可能性があります。この場合、他のすべてを再スローしながら、DocumentNotFoundExceptionを明示的にキャッチして処理できます。 try { collection.get("my-document-id"); } catch (DocumentNotFoundException ex) { createDocument("my-document-id"); } catch (CouchbaseException ex) { throw new DatabaseException("Couchbase lookup failed", ex); } 前述のように、SDKは可能な限り再試行しますが、アプリケーション開発者としての追加のコンテキストがないと、操作が再試行可能かどうかを判断できない場合があります。 たとえば、アプリケーションによっては、特定のドキュメントが1つのアプリによってのみ書き込まれることがわかっているため、失敗した場合にアップサート操作を再試行しても害はありません。 for (int i = 0; i < 10; i++) { try { collection.upsert("docid", JsonObject.create().put("my", "value")); break; } catch (TimeoutException ex) { // propagate, since time budget's up break; } catch (CouchbaseException ex) { System.err.println("Failed: " + ex + ", retrying."); // don't break, so retry } } このコードは、最大10回の試行でドキュメントのアップサートを試みます。このコードはさまざまな方法で改善できますが、再試行のブロックに関する一般的な問題が浮き彫りになります。通常、操作には上限を表す単一のタイムアウトが予想されます。ただし、この場合、常に新しいタイムアウトを発行しているため、個々のタイムアウトは、単一の操作タイムアウトよりもはるかに多くなる可能性があります。 残りのタイムアウトを追跡し、再試行を実行するときに低い値に設定する方法はありますが、高度な再試行が必要な場合は、代わりに次のセクションで説明するリアクティブ再試行を検討することをお勧めします。 リアクティブな例外処理 リアクティブAPIでのエラーの処理は非常に強力ですが、最初は理解するのが少し複雑です。 コードのブロックと同様に、操作が失敗した場合、通常は次の3つの方法があります。 リクエスト元ににエラーを伝播する フォールバックとして別のメソッド/ APIを試す 元の操作を再試行 Reactorでは、エラーは、特別に処理されない限り、オペレーターチェーンを介してサブスクライバーまで移動する信号を終了させます。 collection.reactive().get("this-doc-does-not-exist").subscribe(new Subscriber<GetResult>() { @Override public void onError(Throwable throwable) { // This method will be called with a DocumentNotFoundException } @Override public void onSubscribe(Subscription subscription) { } @Override public void onNext(GetResult getResult) { } @Override public void onComplete() { } }); 最後に.block()(の代わりに.subscribe())が呼び出された場合、エラーがスローされ、try/catchブロックでキャッチできます。 通常、ある時点で修正措置を実行するか、操作を再試行します。前者は、onError*(…​)で始まるさまざまなリアクターメソッドを介して実現できます。 onError*(…​)とdoOnError(…​)を混同しないようにしてください。 前者はオペレーターのシーケンスを積極的に変更しますが、後者は副作用(ロギングなど)を実行するためにのみ使用する必要があり、シーケンスをまったく変更しません。 次の例では、get操作を実行しcreateDocumentReactive、ドキュメントが存在しない場合に呼び出されるフォールバックメソッドに切り替えます。getとupsertは異なる戻りタイプを持っているため、この例ではユーザーに返されるものとして、ドキュメントコンテンツのAPIを使い分ける必要があることに注意してください。 Mono<JsonObject> documentContent = collection.reactive().get("my-doc-id").map(GetResult::contentAsObject) .onErrorResume(DocumentNotFoundException.class, e -> createDocumentReactive("my-doc-id")); 再試行アクションを実行する場合、reactor APIを使用すると、retryWhen(Retry retrySpec)APIを介してこれを非常にエレガントに実行できます。再試行仕様はビルダーのようなAPIであり、いつ、どのように、どのくらいの頻度でプロパティを再試行するかを定義できます。 次のコードはDocumentNotFoundExceptionを最大5回再試行してからあきらめて、エラーを伝播します。 collection.reactive().get("my-doc-id") .retryWhen(Retry.max(5).filter(t -> t instanceof DocumentNotFoundException)); Retryビルダーで利用できるオプションは他にもたくさんあります。詳細については、Reactorの公式ドキュメントを参照してください。 reactor.util.retryパッケージ のRetryクラスを使用することを常にお勧めします。com.couchbase.client.core.retry.reactorパッケージ内のRetryクラスと混同しないでください。これは非推奨であり、ここでの目的には使用しないでください。 RetryStrategyのカスタマイズ カスタムRetryStrategyは、グローバルに有効にすることができます。 ClusterEnvironment environment = ClusterEnvironment.builder().retryStrategy(myCustomStrategy).build(); または、リクエストごとに適用することもできます。 collection.get("doc-id", getOptions().retryStrategy(myCustomStrategy)); どちらのアプローチも有効ですが、ほとんどのユースケースではデフォルトを維持し、リクエストごとにのみオーバーライドすることをお勧めします。 異なる戦略ですべてのリクエストをオーバーライドしていることに気付いた場合は、ローカルに適用するのが理にかなっています。どちらのアプローチでもパフォーマンスに違いはありませんが、リクエストごとにカスタムのものを渡す場合でも、毎回新しいものを作成するのではなく、呼び出し間で共有するようにしてください。 RetryStrategyをゼロから実装することも可能ですが、代わりにBestEffortRetryStrategyを拡張して、カスタマイズが必要な特定のものだけを処理することを強くお勧めします。 class MyCustomRetryStrategy extends BestEffortRetryStrategy { @Override public CompletableFuture<RetryAction> shouldRetry(Request<? extends Response> request, RetryReason reason) { // --- // Custom Logic Here // --- // Do not forget to call super at the end as fallback! return super.shouldRetry(request, reason); } } 重要なのは、return super.shouldRetry(request, reason);部分で、他のすべてのケースが自動的に処理されるように、フォールバックを省略しないでください。 具体的な実装例として、下記のようにサーキットブレイカー構成を使用して、開回路でフェイルファストを実行したい場合があります。 class MyCustomRetryStrategy2 extends BestEffortRetryStrategy { @Override public CompletableFuture<RetryAction> shouldRetry(Request<? extends Response> request, RetryReason reason) { if (reason == RetryReason.ENDPOINT_CIRCUIT_OPEN) { return CompletableFuture.completedFuture(RetryAction.noRetry()); } return super.shouldRetry(request, reason); } } 重要なルールの1つは、shouldRetry処理をブロックしないことです。これは、ホットコードパスで呼び出され、パフォーマンスに大きな影響を与える可能性があるためです。これが、戻り型として定義されている理由であり、非同期のレスポンス型であることを示します。 再試行の決定を行うためにネットワークまたはファイルシステムを介して外部システムを呼び出す必要がある場合は、別のスレッドからこれを実行し、たとえばAtomicを介して通信することをお勧めします。これにより、ホットコードパスでルックアップを実行できます。 RetryActionは、あなたがリクエストで何をすべきかを示します。もしも、あなたがRetryAction.noRetry()を戻すならば、Orchestratorは、その結果、要求をキャンセルしRequestCanceledExceptionを発行します。もう1つのオプションはRetryAction withDuration(Duration duration)、を介して呼び出すことです。これは、要求を次に再試行する必要がある期間を示します。これにより、要求を再試行する必要があるかどうかだけではなく、また、何時再試行するべきかを示すことができます。 TIPS: SDKのAPIによる副作用有無の確認など idempotent()を介してリクエストがべき等であるかどうかを確認することができます。 allowsNonIdempotentRetry()を介して非べき等の再試行が許可されているかどうかを確認することができます。 APIではありませんが、BestEffortRetryStrategyの実装をガイダンスとして用いることも考えられます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[備忘録]STSの起動ができなったときの対処法

目的 読書記録のアプリケーションを作成しようとSTSで新規プロジェクトを立ち上げたが、アプリケーションの起動ができなかった。 すぐに解決ができずにいたため、その対処法を備忘録として残したい。 エラー文 [selection does not contain a main type] 結果 結論:HelloWorldApplication.javaを誤って消してしまっていた。 SpringBootで開発したアプリケーションを起動するクラスを削除していたため、起動ができなかった。 内容は以下の通り。 HelloWorldApplication.java package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HelloWorldApplication { public static void main(String[] args) { SpringApplication.run(HelloWorldApplication.class, args); } } GitHubで削除前のデータは残っていたが、折角なので自分で作成したところ、うまく起動した。 感想 [HelloWorldApplication.java]を消したらアプリケーションが起動しないこと自体は覚えていたが、実際に消したときのエラーに遭遇してみて、すぐに[HelloWorldApplication.java]にたどり着けなかった。比較的簡単に解決したが、注意を払ってSTSの操作をしたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む