- 投稿日:2020-03-01T23:09:08+09:00
bitFlyer Lightning エラーコード集
bitFlyer Lightning エラーコード集
bitFlyer LightningのAPIを叩いていて発生したエラーコードとその対応策をまとめる
- {"status":-100,"error_message":"Invalid product","data":null}
【原因】リクエストヘッダにコンテンツタイプの指定なし
privateAPIを利用する際、リクエストボディにJSON形式の値を詰めると思うが、その値がどのような形式かを明示的に指定してやる必要がある
connection = (HttpURLConnection) url.openConnection(); (中略) connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
- 投稿日:2020-03-01T22:16:32+09:00
BeamのDoFn/SimpleFunction/SerializableFunction
DoFn/SimpleFunction/SerializableFunctionは、Beamの抽象クラス/インターフェイスです。
機能が似ており、時々忘れるのでメモです。DoFn
説明
ParDo.ofの引数に指定してTransformを定義するためのクラスです。
アノテーションを設定し制御することが出来ます。
- ProcessElement
- 入力から出力の変換処理(任意の数の出力)。
- StartBundle/FinishBundle
- bundle単位の処理
- Setup/Teardown
DoFnが満たすべき性質などについて、ParDoのドキュメントに補足が記載されています。
- シリアライザブル
- 無名クラス(=内部クラス)でDoFnを継承すると、外側のクラスへの参照を持つので気をつけよう
- グローバルな状態を持たない
- なんかやり取りしたい時はPCollection通してやろう
- フォールトトレランス性
- 再実行されることはありうるので、冪等になるようなDoFnにしよう
- (無理な時はDoFnの外側のどこかで、カバーする必要)
- 最適化
- Beam RunnerがParDo同士をくっつけて(fusion)、最適化することもあるよ
- くっつけやすいように、また、プログラムが理解しやすいように、DoFnは一つのタスクだけに集中した方がよい
- Dataflowのfusionの説明
使用例
SimpleFunction
説明
名前の通り、シンプルなメソッドを定義する抽象クラスです。
使用例
- MapElements.via
- 1:1のTransformを定義するメソッドです
- WordCountでも使われています
SerializableFunction
説明
- 名前の通り、Serializableである必要があります
- さらに検査例外を投げることも出来ません
- 検査例外投げたい時は、ProcessFunctionを使えとのこと
使用例
- FileIO#by
- 動的に送り先を指定するメソッドです
- 投稿日:2020-03-01T21:03:58+09:00
[Ubuntu]Nukkitのサーバーを立てる
NukkitをUbuntuで立てます
半分前記事のコピーですサーバー環境
・ConoHa VPS
・OS Ubuntu 18.04
・メモリ 512MB
・ストレージ SSD 20GB
・CPU 1coreJavaインストール
Nukkitの動作にはJavaが必要です
今回は例としてopenjdk-11-jreをインストールします$ sudo apt-get install openjdk-11-jreポート開放
今回は例として19132ポート。
コマンドで打つべきなのは#とか$が先頭についてる奴だけです
つまり下でRules updatedって書いてあるのは打たなくていいんですわよ$ ufw allow 19132/udp Rules updatedユーザー作成
今回は例として
nukkitという名前のユーザーを作ります$ adduser nukkit Adding user `nukkit' ... Adding new group `nukkit' (1004) ... Adding new user `nukkit' (1000) with group `nukkit' ... Creating home directory `/home/nukkit' ... Copying files from `/etc/skel' ... Enter new UNIX password:こうなったら新ユーザーのパスワード打ってください
打っても何も表示されませんよ。勘違いしないでくださいねRetype new UNIX password:もう一度同じパスワードを打ってください
passwd: password updated successfully Changing the user information for pmmp Enter the new value, or press ENTER for the default Full Name []: Room Number []: Work Phone []: Home Phone []: Other []:
Full Name []:からOther []:まではそのままEnterで大丈夫ですIs the information correct? [Y/n]と聞かれたら
Yと入力。Sudo
Sudoグループに入れときます。意味わからなかったらMr.Googleに
$ gpasswd -a nukkit sudo Adding user nukkit to group sudoNukkitインストール
まずはNukkit用のディレクトリ(フォルダー)の
nukkitを作ります$ su - nukkit $ mkdir nukkit $ cd nukkitインストール
インストールします
$ wget https://ci.nukkitx.com/job/NukkitX/job/Nukkit/job/master/lastSuccessfulBuild/artifact/target/nukkit-1.0-SNAPSHOT.jarここからはPMMPと異なります
まず、起動するためのシェルスクリプト(使い方あってる?)を作ります$ vi start.sh // viを使って↓を張り付けてください。viの使い方はMr.Googleにask! java -jar nukkit-1.0-SNAPSHOT.jar起動
$ ./start.sh※もし
./start.sh: line 1: java: command not foundと表示されたら一番上のJavaインストールをやり直してみて下さい
※2もし-su: ./start.sh: Permission deniedと表示されたら権限設定がうまくいっていないのでchmodを使ってなんとかしてください(丸投げ)11:41:52 [INFO ] Welcome! Please choose a language first! 11:41:52 [INFO ] eng => English 11:41:52 [INFO ] chs => 中文(?体) 11:41:52 [INFO ] cht => 中文(繁體) 11:41:52 [INFO ] jpn => 日本語 11:41:52 [INFO ] rus => Pycc?ий 11:41:52 [INFO ] spa => Espanol 11:41:52 [INFO ] pol => Polish 11:41:52 [INFO ] bra => Portugues-Brasil 11:41:52 [INFO ] kor => ??? 11:41:52 [INFO ] ukr => Укра?нська 11:41:52 [INFO ] deu => Deutsch 11:41:52 [INFO ] ltu => Lietuvi?kai 11:41:52 [INFO ] idn => Indonesia 11:41:52 [INFO ] cze => Czech 11:41:52 [INFO ] tur => Turkish 11:41:52 [INFO ] fin => Suomi > jpn 11:42:53 [INFO ] Loading nukkit.yml ... 11:42:53 [INFO ] Loading server.properties ... 11:42:53 [INFO ] 日本語 (jpn) を言語に選択しました 11:42:53 [INFO ] Minecraft: BEサーバー(v1.14.0に対応)を起動しています 11:42:53 [INFO ] Selected Zlib Provider: 2 (cn.nukkit.utils.ZlibThreadLocal) > WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance. 11:42:53 [INFO ] 0.0.0.0:19132上でサーバーを開始しています 11:42:53 [INFO ] このサーバーはNukkitのバージョンgit-db2afe0「」(API 1.0.9)で動 作しています 11:42:53 [INFO ] NukkitはLGPLライセンスに基づき配布されています 11:42:55 [INFO ] Loading recipes... 11:42:55 [INFO ] Loaded 1320 recipes. 11:42:55 [INFO ] Successfully loaded 0 resource packs 11:42:55 [WARN ] ワールド "world" が見つかりません > WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by io.netty.util.internal.PlatformDependent0$1 (file:/home/nukkit/nukkit/nukkit-1.0-SNAPSHOT.jar) to field java.nio.Buffer.address WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.PlatformDependent0$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release 11:42:55 [INFO ] ワールド "world" を読み込んでいます 11:42:56 [WARN ] ワールド "nether" が見つかりません 11:42:56 [INFO ] No level called "nether" found, creating default nether level. 11:42:56 [INFO ] ワールド "nether" を読み込んでいます 11:42:56 [INFO ] Epoll Status is true 11:42:56 [INFO ] GS4ステータス リスナーを開始 11:42:56 [INFO ] クエリポートを設定: 19132 11:42:56 [INFO ] クエリーは 0.0.0.0:19132 で動作しています 11:42:56 [INFO ] デフォルトゲームタイプ: サバイバルモード 11:42:56 [INFO ] 起動完了(63.552秒)! "help"または"?"でヘルプを表示途中のエラーの意味は分かりません
ほっといてもいいそうです(PMMPer B氏曰く)
設定はnukkit/nukkit.ymlからどうぞ
できたぞぉぉぉぉScreen を使う
Screenを使うことでPMMPをバックグラウンドで常時起動させておくことができます
インストール↓$ sudo apt-get install screenセッション
// セッションを開始する $ screen -S pmmp $ ./start.sh // セッションに接続する $ screen -r pmmp // セッションから離脱する Ctrl + A + D キーを同時押し終わり
グダグダ+適当でごめんなさい
間違ってるとこだらけだと思うんでなんかあったら教えてください
- 投稿日:2020-03-01T13:25:10+09:00
【2020年度版】Struts2の入門系記事まとめ
はじめに
業務系プロジェクトでは未だに現役で動いていることもあるStruts2さん。
2020年のこのご時世にまさか自分が触ることになろうとは…。まったく触ったことがないのでHelloWorldをやってみた。
割と最近まとめてくれている優しい方がいたので触る分には困らなかった。
参考になった記事を紹介する。自分のスペック
- Java経験が4年くらい
- Webシステム開発は1年くらい
(SpringBootは分かる。新人の頃にSpringMVCも使っていたがよくわからない)- ぶっちゃけサーブレットとJSPはあんまり分からない
APIの開発はできるけど、テンプレートを使うやり方がよく分かってない。
環境情報
- Windos10
- Eclipse : 2019-12(Pleiades All in One)
- XAMPP : XAMPP for Windows 7.3.11
- Java : 1.8.0_221
- Struts : 2.5.22(最新)
参考記事集
【超初心者向け】Struts2超入門 - 2018年版 - Qiita
これ見れば終わり。
全部書いてくれている。2018年度版らしい。
違っていたのはバージョンくらいかな?
とりあえずこれ読んで触ってみれば、会社に行っても話が通じないレベルは脱せられそう。
記事ではallを使っているが、自分が構築したときは最小構成(min)のStrutsで起動できた。Apache Strutsプロジェクトへようこそ
Strutsの公式ページ。
ドキュメントやチュートリアルがある(チュートリアルやってないけど)。
もちろんダウンロードもできる。Struts2 を手動でwarファイルにしてデプロイするには - Qiita
eclipseでは実行できるけど、他の環境へのデプロイは分からないプログラマにならないために。
プロジェクト構成が違ったのか、記事のjarコマンドだけだとclassesがデプロイできなかった。
ただ構成ツリーをサンプルで記載してくれているので合わせたらできた。2014年度版 Eclipse + Struts2 による Java Web アプリ開発入門 | CYOKODOG
流し読みしかしていないが、1番上の記事の方が参考にされていた。
サンプルページがあって参考になる。作ってみたプロジェクトのソース
おわりに
SpringBootの方が簡単に作れるので、メインフレームワークとして使う気は今のところないがなんとなく触りは理解できた。
レガシーで情報がないことを覚悟していたが、概要をまとめてくれている記事があって本当に助かった。
なので自分も現在の最新状態で作れることを記事として残しておくことにした。
有用なリンクが他にあればぜひコメント等で教えていただけると幸いです。
- 投稿日:2020-03-01T12:25:45+09:00
Javaのチェック処理あれこれ
はじめに
プログラムを作成する上でチェック処理は欠かせませんが、ここではJavaにおけるチェック処理あれこれをまとめました。
前提条件
環境:Spring Framework
チェック処理
オブジェクト全般チェック
import java.util.Objects; class Check { void execute() { // message == null if (Objects.isNull(message)) { } // message != null if (Objects.nonNull(message)) { } // message == nullの場合にNullPointerExceptionを投げる Objects.requireNonNull(message); } }文字列チェック
import org.springframework.util.StringUtils; class Check { void execute() { // (message == null || "".equals(message)) if (StringUtils.isEmpty(message)) { } // (str != null && !str.isEmpty()) if (StringUtils.hasLength(message)) { } } }Collectionのチェック
import org.springframework.util.CollectionUtils; class Check { void execute() { List<String> list = new ArrayList<>(); list.add("Hello"); list.add("World"); // (list == null || list.isEmpty()) if (CollectionUtils.isEmpty(list)) { } } }
- 投稿日:2020-03-01T04:18:57+09:00
SeleniumでShiftRightとShiftLeftを使い分ける
3行でわかる概要
- JavaのSelenium(4.0.0-alpha-4)の
WebElement::sendKeysで左右Shiftキーを使い分けようとした際に少し詰まった- 用意されている
KeysはSHIFT/LEFT_SHIFTの中身が同じKeysを使わずに文字列で"ShiftRight"と指定すると右Shiftとして認識される問題
JavaでSeleniumを使用してkeyDownさせる場合、下記のようなコードを書く。
public void sendKey(WebDriver webDriver, CharSequence... keys) { WebElement element = webDriver.findElement(By.className("hoge")) element.sendKeys(keys); }
WebElement.sendKeysは引数にCharSequence...を取るが、用意されているenumKeysを使用することが多い。
ただし、右Shiftキー/左Shiftキーのように同一機能のキーについて左右で使い分けたい場合は注意が必要。enum
Keysの実装は下記のようになっている。
SHIFT/LEFT_SHIFTがそれぞれ定義されているが、残念なことにどちらを送った場合でも左Shiftキーの挙動になってしまう。Keys.javapublic enum Keys implements CharSequence { ... SHIFT ('\uE008'), LEFT_SHIFT (Keys.SHIFT), CONTROL ('\uE009'), LEFT_CONTROL (Keys.CONTROL), ALT ('\uE00A'), LEFT_ALT (Keys.ALT), LEFT ('\uE012'), ARROW_LEFT (Keys.LEFT), UP ('\uE013'), ARROW_UP (Keys.UP), RIGHT ('\uE014'), ARROW_RIGHT (Keys.RIGHT), DOWN ('\uE015'), ARROW_DOWN (Keys.DOWN), ... }解決策
KeysはCharSequenceを実装しており、コンストラクタおよびtoString()の実装は下記のようになっている。Keys.javapublic enum Keys implements CharSequence { ... private final char keyCode; private final int codePoint; Keys(Keys key) { this(key.charAt(0)); } Keys(char keyCode) { this.keyCode = keyCode; this.codePoint = String.valueOf(keyCode).codePoints().findFirst().getAsInt(); } ... @Override public String toString { return String.valueOf(keyCode); } }また、
WebElement.sendKeysは実質与えられたkeycodeを改行区切りで結合してWebDriverに送っているだけ。
そこで、Keys.SHIFTで定義されたKeyCodeを使用せずに下記のように文字列で指定することで正常に認識されるようになる。WebElement element = webDriver.findElement(By.className("hoge")) element.sendKeys("ShiftRight");
- 投稿日:2020-03-01T02:48:12+09:00
WebFluxのフィルタを実装する(WebFlux基本編)
引き続きWebFluxの話です。
前回までで主にController〜Service層でのリアクティブプログラミングの実装サンプルを紹介しました。今回はリクエストの前後の共通処理として利用するフィルタの実装例を紹介します。
FilterではなくWebFliterを実装する
Spring MVCでは
javax.servlet.Filterを実装しますが、WebFluxではorg.springframework.web.server.WebFilterを実装します。以下に、リクエスト処理の前後でログ出力するだけの、簡単なフィルタをそれぞれ実装してみました。
違いをみてみましょう。Spring MVCのFilter実装例
リクエスト処理の前後にログを出力するフィルタ(Filter)@Slf4j public class TimestampFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; // before doBeforeRequest(req); try { // do request chain.doFilter(request, response); // after (success) doAfterRequest(req); } catch (Throwable throwable) { // after (with error) doAfterRequestWithError(req, throwable); throw e; } } private void doBeforeRequest(HttpServletRequest request) { String uri = request.getRequestURI(); long start = System.currentTimeMillis(); // start log(ex: /hoge [IN]: 1582989973940) log.info(String.format("%s [IN]: %d", uri, start)); } private void doAfterRequest(HttpServletRequest request) { String uri = request.getRequestURI(); long end = System.currentTimeMillis(); // end log(ex: /hoge [OUT]: 1582989974053) log.info(String.format("%s [OUT]: %d", uri, end)); } private void doAfterRequestWithError(HttpServletRequest request, Throwable throwable) { String uri = request.getRequestURI(); long end = System.currentTimeMillis(); // end with error log(ex: /hoge [OUT]: 1582989974053 with Error(java.lang.RuntimeException)) log.info(String.format("%s [OUT]: %d with Error(%s)", uri, end, throwable.toString())); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }これと同等の処理を WebFilter で実装したのが以下です。
Spring WebFluxのWebFilter実装例
リクエスト処理の前後にログを出力するフィルタ(WebFilter)@Slf4j @Component public class TimestampFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { return chain.filter(exchange).compose(call -> doFilter(exchange, call)); } private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) { // before return Mono.fromRunnable(() -> doBeforeRequest(exchange)) // do request .then(call) // after (success) .doOnSuccess((done) -> doAfterRequest(exchange)) // after (with error) .doOnError((throwable -> doAfterRequestWithError(exchange, throwable))); } private void doBeforeRequest(ServerWebExchange exchange) { String uri = exchange.getRequest().getURI().toString(); long start = System.currentTimeMillis(); // start log(ex: /hoge [IN]: 1582989973940) log.info(String.format("%s [IN]: %d", uri, start)); } private void doAfterRequest(ServerWebExchange exchange) { String uri = exchange.getRequest().getURI().toString(); long end = System.currentTimeMillis(); // end log(ex: /hoge [OUT]: 1582989974053) log.info(String.format("%s [OUT]: %d", uri, end)); } private void doAfterRequestWithError(ServerWebExchange exchange, Throwable throwable) { String uri = exchange.getRequest().getURI().toString(); long end = System.currentTimeMillis(); // end with error log(ex: /hoge [OUT]: 1582989974053 with Error(java.lang.RuntimeException)) log.info(String.format("%s [OUT]: %d with Error(%s)", uri, end, throwable.toString())); } }
Mono.fromRunnable(~)は、戻り値の無い処理(void)をリアクティブに処理し、その後続をthen(~)に記述します。
例では .then(call) を指定し、次のフィルタまたはコントローラに処理を移譲しています。さらに後続を
doOnSuccess(~)、doOnError(~)で繋げることで、事後処理を実装しています。最後に、もう少し実践的なフィルタの実装サンプルを見てみましょう。
カスタム認証フィルタの実装例
以下は、保護されたリソースへのアクセス時にユーザ認証済みであるかをチェックする、いわゆる認証チェックフィルタの実装例です。
カスタム認証チェックフィルタ@Slf4j @Component @Order(1) public class AuthFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { return chain.filter(exchange).compose(call -> doFilter(exchange, call)); } private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) { return exchange.getSession().flatMap(webSession -> { Map<String, Object> attributes = webSession.getAttributes(); // セッションからユーザ情報を取得 Optional<User> optionalUser = Optional.ofNullable((User)attributes.get(User.class.getName())); return optionalUser // ユーザがある場合はリクエストを処理 .map(user -> call) // ユーザが無い場合は未認証エラーをスロー .orElseThrow(UnauthorizedException::new); }); } }最初のセッション情報の取得
exchange.getSession()はMono<WebSession>を返すため、後続の書き方は以前紹介した 複数の順次APIコール が参考になるかと思います。また、この例では未認証時はカスタムエラー(UnauthorizedException)をスローするので、共通のExceptionHandlerで
401:Unauthrizedレスポンスに変換してあげる想定です。もし、フィルタ内でレスポンスまで生成したい場合は以下のように実装できます。
WebFilter内でレスポンスまで生成...省略... private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) { return exchange.getSession().flatMap(webSession -> { Map<String, Object> attributes = webSession.getAttributes(); // セッションからユーザ情報を取得 Optional<User> optionalUser = Optional.ofNullable((User)attributes.get(User.class.getName())); return optionalUser // ユーザがある場合はリクエストを処理 .map(user -> call) // do request // ユーザが無い場合は未認証エラーレスポンスを生成 .orElse(writeUnauthorizedResponse(exchange)); }); } private Mono<Void> writeUnauthorizedResponse(ServerWebExchange exchange) { // 401:UnauthorizedエラーとしてJSONレスポンスを生成 ServerHttpResponse response = exchange.getResponse(); String body = "{\"message\":\"ログイン認証が必要です。\"}"; return writeResponse(response, HttpStatus.UNAUTHORIZED, body); } private Mono<Void> writeResponse(ServerHttpResponse response, HttpStatus status, String jsonBody) { response.setStatusCode(status); response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8"); DataBufferFactory dbf = response.bufferFactory(); return response.writeWith(Mono.just(dbf.wrap(jsonBody.getBytes()))); }今回のまとめ
WebFluxでフィルタは
WebFilterのサブクラスとして実装する- 中身はやはりリアクティブに処理を記述する (前々回の記事を参考)
となります。
- 投稿日:2020-03-01T02:48:12+09:00
WebFluxのフィルタを実装してみる
引き続きWebFluxの話です。
前回までで主にController〜Service層でのリアクティブプログラミングの実装を紹介しました。今回はリクエストの前後の共通処理として利用するフィルタの実装例を紹介します。
FilterではなくWebFliterを実装する
Spring MVCでは
javax.servlet.Filterを実装しますが、WebFluxではorg.springframework.web.server.WebFilterを実装します。以下に、リクエスト処理の前後でログ出力するだけの、簡単なフィルタをそれぞれ実装してみました。
違いをみてみましょう。Spring MVCのFilter実装例
リクエスト処理の前後にログを出力するフィルタ(Filter)@Slf4j public class TimestampFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; // before doBeforeRequest(req); try { // do request chain.doFilter(request, response); // after (success) doAfterRequest(req); } catch (Throwable throwable) { // after (with error) doAfterRequestWithError(req, throwable); throw throwable; } } private void doBeforeRequest(HttpServletRequest request) { String uri = request.getRequestURI(); long start = System.currentTimeMillis(); // start log(ex: /hoge [IN]: 1582989973940) log.info(String.format("%s [IN]: %d", uri, start)); } private void doAfterRequest(HttpServletRequest request) { String uri = request.getRequestURI(); long end = System.currentTimeMillis(); // end log(ex: /hoge [OUT]: 1582989974053) log.info(String.format("%s [OUT]: %d", uri, end)); } private void doAfterRequestWithError(HttpServletRequest request, Throwable throwable) { String uri = request.getRequestURI(); long end = System.currentTimeMillis(); // end with error log(ex: /hoge [OUT]: 1582989974053 with Error(java.lang.RuntimeException)) log.info(String.format("%s [OUT]: %d with Error(%s)", uri, end, throwable.toString())); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }これと同等の処理を WebFilter で実装したのが以下です。
Spring WebFluxのWebFilter実装例
リクエスト処理の前後にログを出力するフィルタ(WebFilter)@Slf4j @Component public class TimestampFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { return chain.filter(exchange).compose(call -> doFilter(exchange, call)); } private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) { // before return Mono.fromRunnable(() -> doBeforeRequest(exchange)) // do request .then(call) // after (success) .doOnSuccess((done) -> doAfterRequest(exchange)) // after (with error) .doOnError((throwable -> doAfterRequestWithError(exchange, throwable))); } private void doBeforeRequest(ServerWebExchange exchange) { String uri = exchange.getRequest().getURI().toString(); long start = System.currentTimeMillis(); // start log(ex: /hoge [IN]: 1582989973940) log.info(String.format("%s [IN]: %d", uri, start)); } private void doAfterRequest(ServerWebExchange exchange) { String uri = exchange.getRequest().getURI().toString(); long end = System.currentTimeMillis(); // end log(ex: /hoge [OUT]: 1582989974053) log.info(String.format("%s [OUT]: %d", uri, end)); } private void doAfterRequestWithError(ServerWebExchange exchange, Throwable throwable) { String uri = exchange.getRequest().getURI().toString(); long end = System.currentTimeMillis(); // end with error log(ex: /hoge [OUT]: 1582989974053 with Error(java.lang.RuntimeException)) log.info(String.format("%s [OUT]: %d with Error(%s)", uri, end, throwable.toString())); } }
Mono.fromRunnable(~)は、戻り値の無い処理(void)をリアクティブに処理し、その後続をthen(~)に記述します。
例では .then(call) を指定し、次のフィルタまたはコントローラに処理を移譲しています。さらに後続を
doOnSuccess(~)、doOnError(~)で繋げることで、事後処理を実装しています。最後に、もう少し実践的なフィルタの実装サンプルを見てみましょう。
カスタム認証フィルタの実装例
以下は、保護されたリソースへのアクセス時にユーザ認証済みであるかをチェックする、いわゆる認証チェックフィルタの実装例です。
カスタム認証チェックフィルタ@Slf4j @Component @Order(1) public class AuthFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { return chain.filter(exchange).compose(call -> doFilter(exchange, call)); } private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) { return exchange.getSession().flatMap(webSession -> { Map<String, Object> attributes = webSession.getAttributes(); // セッションからユーザ情報を取得 Optional<User> optionalUser = Optional.ofNullable((User)attributes.get(User.class.getName())); return optionalUser // ユーザがある場合はリクエストを処理 .map(user -> call) // ユーザが無い場合は未認証エラーをスロー .orElseThrow(UnauthorizedException::new); }); } }最初のセッション情報の取得
exchange.getSession()はMono<WebSession>を返すため、後続の書き方は以前紹介した 複数の順次APIコール が参考になるかと思います。また、この例では未認証時はカスタムエラー(UnauthorizedException)をスローするので、共通のExceptionHandlerで
401:Unauthrizedレスポンスに変換してあげる想定です。もし、フィルタ内でレスポンスまで生成したい場合は以下のように実装できます。
WebFilter内でレスポンスまで生成...省略... private Publisher<Void> doFilter(ServerWebExchange exchange, Mono<Void> call) { return exchange.getSession().flatMap(webSession -> { Map<String, Object> attributes = webSession.getAttributes(); // セッションからユーザ情報を取得 Optional<User> optionalUser = Optional.ofNullable((User)attributes.get(User.class.getName())); return optionalUser // ユーザがある場合はリクエストを処理 .map(user -> call) // do request // ユーザが無い場合は未認証エラーレスポンスを生成 .orElse(writeUnauthorizedResponse(exchange)); }); } private Mono<Void> writeUnauthorizedResponse(ServerWebExchange exchange) { // 401:UnauthorizedエラーとしてJSONレスポンスを生成 ServerHttpResponse response = exchange.getResponse(); String body = "{\"message\":\"ログイン認証が必要です。\"}"; return writeResponse(response, HttpStatus.UNAUTHORIZED, body); } private Mono<Void> writeResponse(ServerHttpResponse response, HttpStatus status, String jsonBody) { response.setStatusCode(status); response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8"); DataBufferFactory dbf = response.bufferFactory(); return response.writeWith(Mono.just(dbf.wrap(jsonBody.getBytes()))); }今回のまとめ
WebFluxでフィルタは
WebFilterのサブクラスとして実装する- 中身はやはりリアクティブに処理を記述する (前々回の記事を参考)
となります。
- 投稿日:2020-03-01T02:25:38+09:00
Android StudioでExpandableListView(折りたたみリスト)を使う方法
はじめに
折りたたむことができるリストのメモになります。
コード
string.xml<resources> <string name="app_name">ExpandableListView</string> </resources>activity_main.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <ExpandableListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/exListView"></ExpandableListView> </LinearLayout>MainActivity.javapackage com.websarva.wings.android.expandablelistview; import android.app.AlertDialog; import android.content.Context; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.SimpleExpandableListAdapter; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MainActivity extends AppCompatActivity { List<Map<String, String>> parentList; List<List<Map<String, String>>> childList; List<Map<String, String>> childDataList; Map<String, String> childtData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); parentList = new ArrayList<>(); childList = new ArrayList<>(); //親リストに表示する内容を生成 setParentList("parentKey", "親1"); setParentList("parentKey", "親2"); //子リストに表示する内容を生成 String firstItemName1[] = {"子1", "子2"}; String secondItemName1[] = {"子1段目", "子2段目"}; setChildDataList("childKey1", firstItemName1, "childKey2", secondItemName1); String firstItemName2[] = {"子A", "子B", "子C"}; String secondItemName2[] = {"子A段目", "子B段目", "子C段目"}; setChildDataList("childKey1", firstItemName2, "childKey2", secondItemName2); //アダプタ作成 SimpleExpandableListAdapter adapter = new SimpleExpandableListAdapter( this, parentList, android.R.layout.simple_expandable_list_item_1, new String[]{"parentKey"}, new int[]{android.R.id.text1}, childList, android.R.layout.simple_expandable_list_item_2, new String[]{"childKey1", "childKey2"}, new int[]{android.R.id.text1, android.R.id.text2} ); ExpandableListView elv = findViewById(R.id.exListView); elv.setAdapter(adapter); } /** * 親リストに表示する内容を生成する処理 * @param parentKey * @param value * @return parentList */ private List<Map<String, String>> setParentList(String parentKey, String value) { Map<String, String> parentData = new HashMap<String, String>(); parentData.put(parentKey, value); parentList.add(parentData); return parentList; } /** * 子リストに表示する内容を生成する処理 * @param childKey1 * @param firstItemName * @param childKey2 * @param secondItemName * @return childDataList */ private List<Map<String, String>> setChildData(String childKey1, String firstItemName, String childKey2, String secondItemName) { childtData = new HashMap<String, String>(); childtData.put(childKey1, firstItemName); childtData.put(childKey2, secondItemName); childDataList.add(childtData); return childDataList; } /** * 子リストを表示数分作成する処理 * @param childKey1 * @param firstItemName * @param childKey2 * @param secondItemName * @return childList */ private List<List<Map<String, String>>> setChildDataList(String childKey1, String firstItemName[], String childKey2, String secondItemName[]) { childDataList = new ArrayList<>(); //作成する項目数の回数繰り返す for (int i = 0; i < firstItemName.length; i++) { setChildData(childKey1, firstItemName[i], childKey2, secondItemName[i]); } childList.add(childDataList); return childList; } }実行結果
解説
SimpleExpandableListAdapterを使用して、ExpandableListViewにリストとして表示したいデータをセットします。
リストとして表示したいデータをSimpleExpandableListAdapterに格納するために、下記の手順で作成します。手順
1.親リストに表示する内容を生成する処理
2.子リストに表示する内容を生成する処理
3.子リストの項目の数、繰り返す処理
4.親リストに表示するデータをMapに格納する
5.子リストに表示するデータをMapに格納する
6.親リストと、子リストをSimpleExpandableListAdapterへ格納する各データの格納
setParentListメソッドを呼び出した数だけ、親リストを作成する。
引数は、MAPのに格納するキーと値を渡す。setParentList.親データの格納@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); parentList = new ArrayList<>(); ~省略~ //親リストに表示する内容を生成 setParentList("parentKey", "親1"); setParentList("parentKey", "親2"); ~省略~ } private List<Map<String, String>> setParentList(String parentKey, String value) { Map<String, String> parentData = new HashMap<String, String>(); parentData.put(parentKey, value); parentList.add(parentData); return parentList; }setChildDataListメソッドを呼び出した数だけ、親リストを作成する。
引数は、MAPのに格納するキー(第1、第3引数)と値(第2、第4引数)を渡す。setChildDataList.子データの格納@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ~省略~ childList = new ArrayList<>(); ~省略~ //子リストに表示する内容を生成 String firstItemName1[] = {"子1", "子2"}; String secondItemName1[] = {"子1段目", "子2段目"}; setChildDataList("childKey1", firstItemName1, "childKey2", secondItemName1); String firstItemName2[] = {"子A", "子B", "子C"}; String secondItemName2[] = {"子A段目", "子B段目", "子C段目"}; setChildDataList("childKey1", firstItemName2, "childKey2", secondItemName2); ~省略~ } private List<Map<String, String>> setChildData(String childKey1, String firstItemName, String childKey2, String secondItemName) { childtData = new HashMap<String, String>(); childtData.put(childKey1, firstItemName); childtData.put(childKey2, secondItemName); childDataList.add(childtData); return childDataList; } private List<List<Map<String, String>>> setChildDataList(String childKey1, String firstItemName[], String childKey2, String secondItemName[]) { childDataList = new ArrayList<>(); //作成する項目数の回数繰り返す for (int i = 0; i < firstItemName.length; i++) { setChildData(childKey1, firstItemName[i], childKey2, secondItemName[i]); } childList.add(childDataList); return childList; }アダプタの格納
親、子のデータをアダプタに格納する
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ~省略~ //アダプタ作成 SimpleExpandableListAdapter adapter = new SimpleExpandableListAdapter( this, ・・・① parentList, ・・・② android.R.layout.simple_expandable_list_item_1, ・・・③ new String[]{"parentKey"}, ・・・④ new int[]{android.R.id.text1}, ・・・⑤ childList, ・・・② android.R.layout.simple_expandable_list_item_2, ・・・③ new String[]{"childKey1", "childKey2"}, ・・・④ new int[]{android.R.id.text1, android.R.id.text2} ・・・⑤ ); ExpandableListView elv = findViewById(R.id.exListView); elv.setAdapter(adapter); }①ExpandableListViewが関連付けられるContext
②List
③各行のレイアウトを表すR値
④MAPのキー
⑤レイアウト内での文字を表示するTextViewのID以上、ExpandableListView(折りたたみリスト)の使用方法になります。
