- 投稿日:2020-04-09T19:58:10+09:00
リバースプロキシを設定しててもSpringでFlashAttributeしたい!(しない)
結論
アクセスするURLとSpringが受け取るリクエストURLが異なると、FlashAttributeしてもリダイレクト先にパラメータを渡せないぞ!!
事の始まり
例えば、
domain/~
のリクエストをdomain/hoge/~
に変更するプロキシが組まれていたとします。
きっとこんな感じ(適当)
nginx.conflocation / { proxy_pass http://tomcat:8080/hoge/; }その中で、RedirectAttributesのaddFlashAttributeを実行したところ、リダイレクト先でマッピングされない事態に遭遇しました。
たぶんこんな感じ(さらに適当)
HogeController.java@RequestMapping(value="/hoge/save", method = RequestMethod.POST) public String save(@ModelAttribute("form") HogeForm form, RedirectAttributes redirectAttributes) { : : redirectAttributes.addFlashAttribute("form", form); return "redirect:/complete"; } @RequestMapping(value="/hoge/complete", method = RequestMethod.GET) public String complete(@ModelAttribute("form") HogeForm form) { log.info(form); // 空っぽ!! return "hoge.html"; }FlashAttributeなんて余裕やろ!と思っていた矢先に起きた問題。
リバースプロキシ方面から調査したのですが、原因が全くわからず。。。
![]()
![]()
![]()
![]()
数日後
「RedirectAttributesがダメなら、FlashMapを直接使えばいいじゃない」
なんという暴論!と思いながら実装方法を眺めていると、setTargetRequestPathというメソッドを呼んでいる。。。
・・・ターゲットの・・・・パス?
説明しよう!!
何が起こっていたかというと、リダイレクト先のパス(
/complete
)と、リダイレクトされたパス(/hoge/complete
)が不一致であったため、FlashMapの値がバインドされていなかったのだ!!改修例
こういう改修をすれば、リバースプロキシを設定していてもリダイレクト先へパラメータを渡せるようになる。
AbstractController.javaprotected String redirect(String path, Map<String,Object> attributeMap) { // 内部パス(@RequestMapping(value)の値) String innerPath = "/hoge" + path; // FlashMapに詰め替え FlashMap flashMap = new FlashMap(); attributeMap.forEach(flashMap::put); // FlashMapのターゲットを内部パスに設定 flashMap.setTargetRequestPath(innerPath); // FlashMapのセット RequestContextUtils.getFlashMapManager(request).saveOutputFlashMap(flashMap, request, response); // リダイレクト先は外部パス(ブラウザからのアクセス先) return "redirect:" + path; }HogeController.java@RequestMapping(value="/hoge/save", method = RequestMethod.POST) public String save(@ModelAttribute("form") HogeForm form) { : : return redirect("/complete", Map.of("form", form)); }rewriteを使っていて、今回のように内部パスと外部パスが単純に置き換えられない場合も、原理は一緒なので頑張って内部パスと外部パスを指定してください。
FlashMapについては、以下の記事でわかりやすく書いてありました。もっと早く見つけたかった。。。
SpringMVCでRedirectAttributesを使用せずにリダイレクト先に値を渡す
- 投稿日:2020-04-09T11:18:44+09:00
JavaでZIPからFileSystemを呼ぼうとして てこずった話
Java7以降で「ZIP」や「JAR」形式のファイルを「FileSystem」として開けます。
(Java上ではZIPも「JAR」として扱われます。)ところがあるときどうやってもうまくいかなかった。
原因というか結論:「実行環境次第で使えない時がある(?)」備忘録として記事にします。
(環境:Win10で古めのJava8)FileSystems.newFileSystem の使い方
まず、前提となる使用法を記しときます。
FileSystems.newFileSystem()というメソッドを使います。
対象ファイルの指定は Path か URI が使えます。
既存ファイル、新規ファイルどちらでもいけます。1. Path で指定する方法
final Path zipfilePath = Paths.get("C:/a.zip"); try{ FileSystem fs = FileSystems.newFileSystem(zipfilePath, null); }catch(Exception e){ e.printStackTrace(); }ネットで探すと、
↓ このように第2引数にクラスローダを渡すサンプルが多い。
FileSystem fs = FileSystems.newFileSystem(zipfilePath, ClassLoader.getSystemClassLoader() );
この「ClassLoader.getSystemClassLoader()」がわけわからないからあきらめてURI指定に走る使用者が多い様子。
けれどソースを覗いた感じでは「null」で問題ない。2.URI で指定する方法
final Path zipfilePath = Paths.get("C:/a.zip"); Map<String, Object> env = new HashMap<>(); //env.put("create", "true"); //// Enable creation for no-exists path. //env.put("encoding", "UTF-8"); try{ FileSystem fs = FileSystems.newFileSystem( URI.create("jar:" + zipfilePath.toUri().toString() ), env ); }catch(Exception e){ e.printStackTrace(); }URI による指定の場合は第2引数に Map インスタンスを渡します。ターゲットが既存ファイルの場合は空のMapでよい。
それもって URI のほうでスキーム「jar」を指定する。実行したら失敗した(環境依存)
Path指定のときのエラーメッセージjava.nio.file.ProviderNotFoundException: Provider not foundURI指定のときのエラーメッセージjava.nio.file.ProviderNotFoundException: Provider "jar" not found特に URI で指定していると、どこでミスってるかわけわからない。。。
なんで失敗したのか?
原因がわかってから見直すと、エラーメッセージを素直に読んだその通りなのでした。
「jar (すなわち zip) を扱えるプロバイダ」が無い。ここで、「プロバイダ」として実際になにが存在しているのか確認してみます。
ファイルシステムプロバイダを表示するimport java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.spi.FileSystemProvider; class ZipAsFileSystem{ public static final void main(String[] args) { for (FileSystemProvider provider: FileSystemProvider.installedProviders()) { System.out.println("provider = "+provider); System.out.println("scheme = "+provider.getScheme()); } } }まず、エラーがでない環境の実行結果を示します。
実行結果 jdk1.8.0_121provider = sun.nio.fs.WindowsFileSystemProvider@28d93b30 scheme = file provider = com.sun.nio.zipfs.ZipFileSystemProvider@1b6d3586 scheme = jar↑ これが正常。
つぎにエラーが出る環境の実行結果:
実行結果 jre1.8.0_121provider = sun.nio.fs.WindowsFileSystemProvider@45ee12a7 scheme = file↑ こちらは「ZipFileSystemProvider」が見つかってない。
だからZIPをハンドルできない。
どうりですね。上の2つの環境は どちらも同じバージョンのOracleのjavaです。
違いは
・JDK同梱のランタイム(正常動作)
・JREとして配布されているランタイム(失敗)
このバージョン特有なのか、Oracle特有なのか、あるいは筆者が何か余計なことをしたかは不明です。結論
とにかく
・「ZipFileSystemProvider」が実行環境中に存在していないときがある。
・そのせいでZIPをFileSystemできないときがある。
ということでした。
- 投稿日:2020-04-09T11:18:44+09:00
Java8でZIPからFileSystemを使おうとしててこずった話
Javaでは「ZIP」や「JAR」形式のファイルを「FileSystem」として開けます。
(Java上ではZIPも「JAR」として扱われます。)ところがあるときどうやってもうまくいかなかった。
原因というか結論:「実行環境次第で使えない時がある」備忘録として記事にします。
(環境:Win10でJava8)FileSystems.newFileSystem の使い方
まず、前提となる使用法を記しときます。
FileSystems.newFileSystem()というメソッドを使います。
対象ファイルの指定は Path か URI が使えます。1. Path で指定する方法
final Path zipfilePath = Paths.get("C:/a.zip"); try{ FileSystem fs = FileSystems.newFileSystem(zipfilePath, null); }catch(Exception e){ e.printStackTrace(); }ネットで探すと第2引数にクラスローダを渡すサンプルが多い。
↓ このように:
FileSystem fs = FileSystems.newFileSystem(zipfilePath, ClassLoader.getSystemClassLoader() );
この「ClassLoader.getSystemClassLoader()」がわけわからないからあきらめてURI指定にする使用者が多い模様。
けれどソースを覗いた感じでは「null」で問題ない。2.URI で指定する方法
final Path zipfilePath = Paths.get("C:/a.zip"); Map<String, Object> env = new HashMap<>(); //env.put("create", "true"); //// Enable creation for no-exists path. //env.put("encoding", "UTF-8"); try{ FileSystem fs = FileSystems.newFileSystem( URI.create("jar:" + zipfilePath.toUri().toString() ), env ); }catch(Exception e){ e.printStackTrace(); }URI による指定の場合は第2引数に Map インスタンスを渡します。既存ファイルの場合は空のMapでよい。
それで URI のほうでスキーム「jar」を指定する。実行したら失敗した(環境依存)
Path指定のときのエラーメッセージjava.nio.file.ProviderNotFoundException: Provider not foundURI指定のときのエラーメッセージjava.nio.file.ProviderNotFoundException: Provider "jar" not foundなんで失敗したのか?
エラーメッセージを素直に読んだらその通りなのでした。
「jar すなわち zip を扱えるプロバイダ」が無かった。ここで、「プロバイダ」としてなにが存在しているのか確認してみます。
ファイルシステムプロバイダを表示するimport java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.spi.FileSystemProvider; class ZipAsFileSystem{ public static final void main(String[] args) { for (FileSystemProvider provider: FileSystemProvider.installedProviders()) { System.out.println("provider = "+provider); System.out.println("scheme = "+provider.getScheme()); } } }まず、エラーがでない環境のプロバイダを示します。
実行結果 jdk1.8.0_121provider = sun.nio.fs.WindowsFileSystemProvider@28d93b30 scheme = file provider = com.sun.nio.zipfs.ZipFileSystemProvider@1b6d3586 scheme = jar↑ これが正常。
つぎにエラーが出る環境のプロバイダ
実行結果 jre1.8.0_121provider = sun.nio.fs.WindowsFileSystemProvider@45ee12a7 scheme = file↑ こちらは「ZipFileSystemProvider」が見つかってない。
だからZIPをハンドルできない。どうりですね。2つの環境はどちらも同じバージョンのOracleのjavaです。
違いは
・JDK同梱のランタイム(正常動作)
・JREとして配布されているランタイム(失敗)
このバージョン特有なのか、Oracle特有なのか、あるいは筆者が何か余計なことをしたか不明。
とにかく「ZipFileSystemProvider」が存在していないせいで
ZIPをFileSystemできないときがある
ということでした。
- 投稿日:2020-04-09T00:18:38+09:00
[Processing] GT Forceを使ってみる。
はじめに
今回は、PCでGT Forceを接続してみたという話。Processingという言語を使うと簡単に使えるようになるので、ぜひお試しいただきたい。ゲーム用のコントローラが自由に使えるようになるとやれることが広がるのではないか。今回の内容は「自作のいろいろ」様のサイトの情報をもとに行った。筆者もはっきりこのライブラリを理解していないため、説明が不十分なところがある。筆者のメモ程度の内容であることをご了承いただきたい。
使用するGT ForceはPS2用のもので非常に古いものである。調べてみると、このハンドルコントローラーはWindows10に対応していないとの記述もあったが、GT Force Pro用のものを使用したところ、無事Windows10で使うことができた。ただし、logicoolのセットアップソフトでは、ペダルが反応しなかった。故障かと思ったが後のプログラムでは動作した。
実行環境
・Windows10 64bit
・Processing 3.5.3
・Game Control Plus 1.2.2
・GT Force LPRC-10000方法
①GT Force用ソフトのインストール
ロジクールのサポートサイトより、GT Force Pro用のソフトをインストールする。これをインストールしなくてもUSBをパソコンに刺すだけで認識したが、キャリブレーションなどができるのでお勧めする。ソフトで調整するときハンドルの右後ろのボタンを押したところ、ハンドルが暴走して、回転が止まらなくなったことがあった(笑)。刺しなおして今のところ問題ない。
ロジクールサポートサイト②ライブラリをインストール
まずprocessingを起動し、上タブの「スケッチ」→「ライブラリをインポート」→「ライブラリを追加」より「Game Control Plus」をインストールする。
③サンプルスケッチを用いて動作の確認。
上タブの「ファイル」→「サンプル」から「Game Control Plus」のサンプルデータを確認。「Gcp_Configurator」を選んで起動。つないだデバイス名の左にあるボタンを押すと、詳細画面が開く。そこで各ボタン、レバーの動作の確認ができる。GT Forceでは6つのボタンと4つのスライダーが認識された。しかし4つのうち「Combined pedals」のスライダーは反応しなかった。動作の確認が取れたら、このプログラムを終了する。
次は、同様にサンプルプログラムを用いてコントローラーの情報を得る。これは今後のプログラミングにおいて重要になる。先ほど選んだ「Game Control Plus」のサンプルプログラムの中から「Gcp_ShowDevices」を選んで起動する。このプログラムでは、コントローラーのスライダーやボタンの名前・番号が確認できる。プログラムのスクロールバーの動きが不安定であったが、問題なく次のようなデータが得られた。この情報を記録する。
NAME : Logicool WingMan Formula Force GP USB Type : Wheel Port : Unknown Buttons (6) Type Name Multiplier button Left Paddle - button Right Paddle - button Button 3 - button Button 4 - button Button 5 - button Button 6 - Sliders (4) Type Name Multiplier Tolerance slider Wheel axis 1.0 0.0 absolute slider Combined pedals 1.0 0.0 absolute slider Accelerator 1.0 0.0 absolute slider Brake 1.0 0.0 absoluteプログラミングのときに使うキーワード。
「自作のいろいろ」様のサイトから引用。
ライブラリーをインクルードする。
import net.java.games.input.*; import org.gamecontrolplus.*; import org.gamecontrolplus.gui.*;使用するコントローラーの宣言など
ControlIO control; ControlDevice device; ControlSlider[] sliders = new ControlSlider[4]; ControlButton[] button =new ControlButton[6];これは使用する機器により配列数の変更などが必要。
機器の読み込み(setup()内で宣言する。)
control = ControlIO.getInstance(this); device = control.getDevice("Logicool WingMan Formula Force GP USB");//使用する機器により変更する。スライダーの割り当て(setup()内で宣言する。)
sliders[0] = device.getSlider(0);これで宣言が可能である。先ほど「sliders[]」という配列を作成したので、そこに代入している。ボタン複数の場合は数字を変更する。
スライダーの値を取り出す
sliders[0].getValue()スライダーからの値はこれで簡単に取り出せる。
ボタンの割り当て(setup()内で宣言する。)
button[0] = device.getButton(0); button[0].plug(this, "func1", ControlIO.ON_PRESS);//ボタンを押したときの処理 button[0].plug(this, "func2", ControlIO.ON_RELEASE);//ボタンを離した時の処理ボタンはこのように設定した。それぞれ関数名を設定し、イベントが発生したときに実行させることができる。関数は下記のように宣言する。
void func1(){ //実行させたい処理 } void func2(){ //実行させたい処理 }上記のキーワードを組み合わせることで簡単に実行させることができる。
値の操作
コントローラーから出力される値は正から負の値であったため、値の調整が必要であった。その時は「map」関数を使用すると便利である。xをa→bの範囲からc→dの範囲に変換する場合、
float x = map(x,a,b,c,d);このように簡単に使用できる。
おわりに
このようにとても簡単にゲームコントローラーを使用できるようになる。ProcessingはArduinoともシリアル通信で簡単につなげることができるのでやれることが広がると思った。