- 投稿日:2020-05-28T22:46:41+09:00
Google ColaboratoryでJavaのJupyter Kernelを使う
前回(Google ColaboratoryでJavaプログラムを動かす)の続き
前回との違い
ちゃんとColab上でJavaのJupyter Kernelを使ってpythonを使うのと同じようにJavaを動かす
手順
1. colab上から
.ipynb
をダウンロードする2.
.ipynb
を書き換える元の内容は下記のようになっている
ColabJava.ipynb{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "ColabJava.ipynb", "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" } }, "cells": [ { "cell_type": "code", "metadata": { "id": "hXaCsZGNbimN", "colab_type": "code", "colab": {} }, "source": [ "" ], "execution_count": 0, "outputs": [] } ] }
kernelspec
を書き換える変更後.ipynb... "kernelspec": { "name": "java", "display_name": "Java" } ...Jupyter Kernelを入れる
変更した
.ipynb
をノートブックをアップロードからアップロード
今回はIJavaを使用するので、開いたcolabで下記セルを実行!git clone https://github.com/SpencerPark/IJava.git %cd IJava/ !./gradlew installKernel最初はJava Kernelが無い状態なので下記のようなメッセージが左下に表示される
ので、上のセルが実行完了したらノートブックの設定を改めてJavaに合わせて保存し直す。完全勝利!
おまけ(外部ライブラリを使う)
今回使用したIJavaのRead meにもちゃんと書いてあるが、外部ライブラリも普通に読んで使える。(すごい!)
- 投稿日:2020-05-28T22:09:23+09:00
try-with-resourcesとAutoCloseable
try-with-resourcesは大体IOExceptionが発生するものに対してのみ使えると思っていましたが、。
AutoCloseableで様々なクラスに対して有効化できるらしい。try-with-resourcesについて
try-with-resourcesステートメントは、1つ以上のリソースを宣言するtryステートメントです。 リソースは、プログラムが終了した後に閉じる必要があるオブジェクトです。 try-with-resourcesステートメントは、各リソースがステートメントの最後で確実に閉じられるようにします。 java.io.Closeableを実装するすべてのオブジェクトを含む、java.lang.AutoCloseableを実装するすべてのオブジェクトをリソースとして使用できます。
Oracle Java Documentation
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.htmlつまり、AutoCloseableを実装すればtry-with-resourcesに対応できる!
AutoCloseableについて
閉じられるまでリソース(ファイルまたはソケットハンドルなど)を保持できるオブジェクト。
AutoCloseableオブジェクトのclose()メソッドは、リソース仕様ヘッダーでオブジェクトが宣言されているtry-with-resourcesブロックを終了すると、自動的に呼び出される。
この構造により、迅速なリリースが保証され、リソース不足の例外や、そうでなければ発生する可能性のあるエラーが回避されます。
java.langパッケージに含まれている。Interface AutoCloseable
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/AutoCloseable.htmltry-with-resourcesでtry文が終わった時に自動でクローズ処理してもらいたい、
またクローズ時に特殊な処理を挟んでほしい対象のリソースに対してAutoCloseableインターフェースを実装する。AutoCloseableとCloseableについて
AutoCloseableと似て非なるものでCloseableがある。
こちらはAutoCloseableを継承したインターフェースであり、こちらはjava.ioパッケージに含まれている。
AutoCloseableは例外発生時にExceptionをスローするが、CloseableはIOExceptionをスローする。java.ioパッケージにあるほとんどのクラスでCloseableが実装されている為、
FileOutputStreamやBufferedReaderなどのクラスはtry-with-resourcesで宣言できる。使用
try-with-resourcesで自動的にリソースの開放したい場合、
リソース解放時にほかの処理も加えたい場合などに使用する。SampleTryWithResources.javapublic class SampleTryWithResources { public static void main(String[] args) { // Closeable(AutoCloseable)を実装しているクラスを // try-with-resourcesで宣言すると、try文終了時に自動でclose()が実行される。 try (SampleCloseAble sca = new SampleCloseAble()) { System.out.println("Try-With-Resources開始"); System.out.println("try文実行中"); System.out.println("Try-With-Resources終了"); } catch (Exception e) { e.printStackTrace(); } } } class SampleCloseAble implements AutoCloseable { @Override public void close() throws Exception { System.out.println("Closeableクラスのclose()実行"); } }sys.outTry-With-Resources開始 try文実行中 Try-With-Resources終了 Closeableクラスのclose()実行try文の中の処理が終了した後に実装したcloseメソッドが呼び出されている。
ただし、次のようにtry文の中で例外が発生すると、catch文の前にcloseメソッドが呼び出されることに注意すること。SampleTryWithResources2.javapublic class SampleTryWithResources2 { public static void main(String[] args) { try (SampleCloseAble sca = new SampleCloseAble()) { System.out.println("Try-With-Resources開始"); System.out.println("try文実行中"); throw new Exception(); } catch (Exception e) { System.out.println("Try文例外終了"); e.printStackTrace(); } } }sys.outTry-With-Resources開始 try文実行中 Closeableクラスのclose()実行 Try文例外終了 java.lang.Exception at ...処理の順番は
Try文の中の処理 ⇒ AutoCloseableのcloseメソッド ⇒ catch文の中の処理
となる。
- 投稿日:2020-05-28T21:43:01+09:00
「ソースが見つかりませんでした」eclipseでデバッグした時に出てきたら
eclipseでspringbootを使って開発している駆け出しエンジニアです。
やり方を調べながらデバッグをやろうとした時に出てきたエラーの解決方法についてです。状況・原因
ブレークポイントを立てて、デバッグを起動しました。
ブレークポイントで止まったと思ったら、赤文字で
「ソースが見つかりませんでした」と表示されました。ソースルックアップパスというものに動かしているプロジェクトが含まれていなかったことが原因でした。
解決方法
デバッグの所の虫みたいなマークの隣にある逆三角クリック
→デバッグの構成→ソースと進む
→ソースルックパスで「追加」を押して、使ってるJavaプロジェクトを追加する
僕はこれで無事に解決されました。
試してみてください。
- 投稿日:2020-05-28T21:30:36+09:00
JavaFXのTableViewで最後の列を自動制御にする
概要
JavaFXのTableViewの列幅の制御で、困ったのでそれの対応方法のメモ。
最後の列を自動制御にして、テーブル幅いっぱいに列が埋まるようにしてみた。
ColumnResizePolicy
JavaFxのTableViewの列幅の制御については、ColumnResizePolicyという仕組みがある。
これは、初期描画時や列幅の調整時に、どのように列幅の制御をするかを決めている仕組みになるようだ。デフォルトで用意されているのは2種類
定数 概要 UNCONSTRAINED_RESIZE_POLICY 特に制御は無く、ユーザーが動かした通りに列幅を処理する CONSTRAINED_RESIZE_POLICY 初期描画時などは均等の列幅、列の伸縮時はそれに合わせて、他の列を均等に処理する これ以外で独自の処理が必要な場合は実装する必要があり、実態としてはCallbackクラスとなる。
public final void setColumnResizePolicy(Callback<ResizeFeatures, Boolean> callback)この中で、各列の幅の変更を行い、成功した場合はtrueを返すことで実現しているようだ。
個人的に欲しかったのは、最後の列の幅の自動制御で、以下を実現したい
- 初期描画時、ウィンドウのリサイズ時は、最後の列をウィンドウ幅(ペイン)に合わせたい
- ダブルクリックのオートフィット時にも、最後の列をウィンドウ幅(ペイン)に合わせたいJavaFxのTableView周りを覗いてどうにか出来ないか探したが、デフォルトではないようで、あきらめて実装。
何とか目的の動作になった。細かい制御や考慮はしてないけど、動作確認としては問題なさそう。public class ResizePolicy { public static <S> void setLastColumnResizePolicy(TableView<S> tableView) { // リサイズポリシーをセットする tableView.setColumnResizePolicy(prop -> { if (prop.getTable().getWidth() < 0) { return false; } resizeTargetColumn(prop); resizeLastColumn(prop); return true; }); // ダブルクリック時のオートフィット後のリサイズ処理を追加する addResizeBorderDoubleClickedEvent(tableView); } private static void resizeTargetColumn(TableView.ResizeFeatures<?> prop) { var targetColumn = prop.getColumn(); var delta = prop.getDelta(); if (delta != 0 && targetColumn.isResizable()) { var newWidth = targetColumn.getWidth() + delta; // 最大幅、最小幅を考慮して、最終的な幅を設定する if (newWidth > targetColumn.getMaxWidth()) { targetColumn.setPrefWidth(targetColumn.getMaxWidth()); } else if (newWidth < targetColumn.getMinWidth()) { targetColumn.setPrefWidth(targetColumn.getMinWidth()); } else { targetColumn.setPrefWidth(newWidth); } } } private static void resizeLastColumn(TableView.ResizeFeatures<?> prop) { var table = prop.getTable(); // -18.0 をしないとスクロールバーが出る。18.0は画面を見て決めただけ var tableContentWidth = table.getWidth() - 18.0; // 最後のカラム以外の合計値とテーブルの幅から、最後のカラムの幅を算出する var columns = table.getVisibleLeafColumns(); var columnsOfExcludedLastColumn = columns.stream().limit(columns.size() - 1); var widthsOfExcludedLastColumn = columnsOfExcludedLastColumn.map(TableColumnBase::getWidth); var lastColumnWidth = widthsOfExcludedLastColumn .reduce(tableContentWidth, (remainingWidth, columnWidth) -> remainingWidth - columnWidth); if (lastColumnWidth > 0) { var lastColumn = columns.get(columns.size() - 1); lastColumn.setPrefWidth(lastColumnWidth); } } private static <S> void addResizeBorderDoubleClickedEvent(TableView<S> tableView) { // 画面描画時にはskinがnullなので、イベントとしてやりたいことを登録しておく tableView.skinProperty().addListener((observable, oldValue, newValue) -> { var tableViewSkin = (TableViewSkin<?>) newValue; var tableViewSkinNodes = tableViewSkin.getChildren().stream(); var tableHeaderRowNode = tableViewSkinNodes.filter(node -> node instanceof TableHeaderRow).findFirst(); tableHeaderRowNode.ifPresent((node) -> { var tableHeaderRow = (TableHeaderRow) node; var tableColumnHeader = (NestedTableColumnHeader) tableHeaderRow.getChildren().get(1); tableColumnHeader.addEventFilter(MouseEvent.MOUSE_PRESSED, mouseEvent -> { // カラム幅の変更については、四角形の線が実際のイベント対象になる // かつダブルクリックのオートフィットを対象とする if (isRectangle(mouseEvent) && isDoubleClick(mouseEvent)) { // クリックされた四角形の線の位置と、カラム幅から対象となるカラムを探し出す var rectangle = (Rectangle) mouseEvent.getTarget(); var columnPosition = 0.0; for (var column : tableView.getColumns()) { columnPosition += column.getWidth(); if (rectangle.getLayoutX() < columnPosition) { // このタイミングでリサイズポリシーを実行すると、 // オートフィットによる幅の変更前なので、実際の実行は後回しにする Platform.runLater(() -> { var columnResizePolicy = tableView.getColumnResizePolicy(); columnResizePolicy.call(new TableView.ResizeFeatures<S>(tableView, column, -0.1)); }); break; } } } }); }); }); } private static boolean isRectangle(MouseEvent mouseEvent) { return mouseEvent.getTarget() instanceof Rectangle; } private static boolean isDoubleClick(MouseEvent mouseEvent) { return mouseEvent.getClickCount() == 2 && mouseEvent.isPrimaryButtonDown(); } }本当は、staticではなく、クラスとして作ろうとしたが、ジェネリクスの問題で警告が出て、SuppressWarningsで消すのもなんだかなと思って、staticなメソッドにしてしまった。。。
ResizeFeaturesはジェネリクスのクラスなのに、setColumnResizePolicyメソッドでは、生で扱ってるので、自分で型宣言をちゃんとやると、型エラーで受け入れてくれない。
型を付けないと警告が出る感じです。なんでこうしたんだろうか。。。
- 投稿日:2020-05-28T21:18:32+09:00
Spring Bootで簡易検索アプリ作成
標題の通り、検索アプリを作成しました。
使用環境
・Windows10 (64bit)
・spring-boot:2.2.6
・Eclipse:4.9.0
・H2完成画面
ジャンル、著者、タイトルのいずれかまたは、すべて入力した際に
画面の下部にある内容を引っ張ってきます。
Entity
BookData.javaimport javax.persistence.Entity; import javax.persistence.Id; @Entity public class BookData { @Id private long isbn; private String genre; private String author; private String title; private int stock; private Boolean status; public long getIsbn() { return isbn; } public void setIsbn(long isbn) { this.isbn = isbn; } public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getStock() { return stock; } public void setStock(int stock) { this.stock = stock; } public Boolean getStatus() { return status; } public void setStatus(Boolean status) { this.status = status; } }Dao
ジャンル、著者、タイトルで検索するためDaoクラスを用意します。
BookDataDao.javaimport java.io.Serializable; import java.util.List; public interface BookDataDao extends Serializable { public List<BookData> search(String genre, String author, String title); }Repository
Daoクラスを実装したBookDataDaoImplを作成します。
各記述の内容はコメントを参照ください。BookDataDaoImpl.javaimport java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @Repository public class BookDataDaoImpl implements BookDataDao { //Entityを利用するために必要な機能を提供する @Autowired private EntityManager entityManager; public BookDataDaoImpl() { super(); } public BookDataDaoImpl(EntityManager manager) { this(); entityManager = manager; } //Daoクラスで用意したsearchメソッドをオーバーライドする @SuppressWarnings("unchecked") @Override public List<BookData> search(String genre, String author, String title) { //StringBuilderでSQL文を連結する StringBuilder sql = new StringBuilder(); sql.append("SELECT b From BookData b WHERE "); boolean genreFlg = false; boolean authorFlg = false; boolean titleFlg = false; boolean andFlg = false; //genreがブランクではなかった場合、sql変数にappendする //フラグをtrueにしとく if(!"".equals(genre)) { sql.append("b.genre LIKE :genre"); genreFlg = true; andFlg = true; } //authorがブランクではなかった場合、sql変数にappendする //フラグをtrueにしとく if(!"".equals(author)) { if (andFlg) sql.append(" AND "); sql.append("b.author LIKE :author"); authorFlg = true; andFlg = true; } //titleがブランクではなかった場合、sql変数にappendする //フラグをtrueにしとく if(!"".equals(title)) { if (andFlg) sql.append(" AND "); sql.append("b.title LIKE :title"); titleFlg = true; andFlg = true; } /* QueryはSQLでデータを問い合わせるためのクエリ文に相当する機能を持つ entityManagerのcreateQueryメソッドを使用する sql変数を引数に渡す */ Query query = entityManager.createQuery(sql.toString()); //上記のif文でtrueになった場合は、各変数に値をセットする //今回、あいまい検索したいのでlike句を使用する if (genreFlg) query.setParameter("genre", "%" + genre + "%"); if (authorFlg) query.setParameter("author", "%" + author + "%"); if (titleFlg) query.setParameter("title", "%" + title + "%"); return query.getResultList(); } }BookDataRepository
BookDataRepository.javaimport org.springframework.data.jpa.repository.JpaRepository; public interface BookDataRepository extends JpaRepository<BookData, Long> { }Service
BookService.javaimport java.util.ArrayList; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class BookService { @Autowired private BookDataRepository bookDataRepository; @Autowired private BookDataDaoImpl bookDataDaoImpl; //全件検索 public List<BookData> findAll(){ return bookDataRepository.findAll(); } //該当のID見つける public Optional<BookData> findById(long isbn) { return bookDataRepository.findById(isbn); } //保存 public BookData save(BookData bookData) { return bookDataRepository.saveAndFlush(bookData); } //検索 public List<BookData> search(String genre, String author, String title){ List<BookData> result = new ArrayList<BookData>(); //すべてブランクだった場合は全件検索する if ("".equals(genre) && "".equals(author) && "".equals(title)){ result = bookDataRepository.findAll(); } else { //上記以外の場合、BookDataDaoImplのメソッドを呼び出す result = bookDataDaoImpl.search(genre, author, title); } return result; } }Controller
StockController.javaimport java.util.List; import java.util.Optional; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/book") public class StockController { @Autowired private BookService bookService; @PersistenceContext EntityManager entityManager; //一覧表示処理 @GetMapping public String index(Model model,@ModelAttribute("formModel") BookData bookdata) { model.addAttribute("msg", "在庫管理"); model.addAttribute("msg2", "検索条件を入力してください"); List<BookData> books = bookService.findAll(); model.addAttribute("books", books); return "index"; } //検索結果の受け取り処理 //@ModelAttributeでformからformModelを受け取り、 //その型(BookData)と変数(bookdata)を指定する @PostMapping public String select(@ModelAttribute("formModel") BookData bookdata, Model model) { model.addAttribute("msg", "検索結果"); //bookdataのゲッターで各値を取得する List<BookData> result = bookService.search(bookdata.getGenre(),bookdata.getAuthor(), bookdata.getTitle()); model.addAttribute("books", result); return "index"; } //詳細画面処理 //@PathVariableでURLから受け取った値を取得する @GetMapping("detail/{isbn}") public String detail(@PathVariable long isbn, Model model) { model.addAttribute("msg", "参照画面"); Optional<BookData> data = bookService.findById(isbn); //Optionalを使用する際、値はget()で取得する model.addAttribute("form", data.get()); return "detail"; } //初期化処理 @PostConstruct public void init() { BookData d1 = new BookData(); d1.setAuthor("夏目漱石"); d1.setTitle("こころ"); d1.setGenre("文学"); d1.setIsbn(11111); d1.setStock(100); d1.setStatus(false); bookService.save(d1); BookData d2 = new BookData(); d2.setAuthor("大野次郎"); d2.setTitle("Spring入門"); d2.setGenre("技術"); d2.setIsbn(22222); d2.setStock(1); d2.setStatus(false); bookService.save(d2); BookData d3 = new BookData(); d3.setAuthor("田中太郎"); d3.setTitle("ネットワークのしくみ"); d3.setGenre("技術"); d3.setIsbn(33333); d3.setStock(20); d3.setStatus(false); bookService.save(d3); BookData d4 = new BookData(); d4.setAuthor("吉川量"); d4.setTitle("泥の銃弾"); d4.setGenre("ミステリー"); d4.setIsbn(44444); d4.setStock(99); d4.setStatus(false); bookService.save(d4); BookData d5 = new BookData(); d5.setAuthor("夏目漱石"); d5.setTitle("草枕"); d5.setGenre("文学"); d5.setIsbn(55555); d5.setStock(40); d5.setStatus(false); bookService.save(d5); BookData d6 = new BookData(); d6.setAuthor("テスト敏郎"); d6.setTitle("連続殺人事件簿"); d6.setGenre("ミステリー"); d6.setIsbn(66666); d6.setStock(40); d6.setStatus(false); bookService.save(d6); BookData d7 = new BookData(); d7.setAuthor("蛇場太郎"); d7.setTitle("Java入門"); d7.setGenre("技術"); d7.setIsbn(77777); d7.setStock(40); d7.setStatus(false); bookService.save(d7); } }index.html
index.html<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>初期画面</title> <style> h1 {font-size:18pt; font-weight:bold; color:gray;} body {font-size:13pt; font-weight:bold; color:gray; margin:5px 25px;} tr {margin:5px;} th {padding:5px;color:white; background:darkgray;} td {padding:5px;color:black; background:#f0f0f0;} </style> </head> <body> <h1 th:text="${msg}"></h1> <p th:text="${msg2}"></p> <!--入力した値をth:object="${formModel}"に詰める --> <!--th:valueで初期値が入るようにする --> <form method="post" th:action="@{/book}" th:object="${formModel}"> <label>ジャンル:</label> <input type="text" name="genre" th:value="*{genre}"><p> <label>著者 :</label> <input type="text" name="author" th:value="*{author}"><p> <label>タイトル:</label> <input type="text" name="title" th:value="*{title}"><p> <input type="submit" value="検索"> </form> <table> <tr> <th>ISBN</th> <th>タイトル</th> <th>著者名</th> <th>在庫数</th> <th>ジャンル</th> </tr> <tr th:each="obj:${books}" th:object="${obj}"> <!--th:hrefでリンク設定 --> <td><a th:href="@{/book/detail/{isbn}(isbn=*{isbn})}" th:text="*{isbn}"></a></td> <td th:text="*{title}"></td> <td th:text="*{author}"></td> <td th:text="*{stock}"></td> <td th:text="*{genre}"></td> </tr> </table> </body> </html>処理パターン1
ジャンルに「文学」と入力して、検索ボタン押下。
検索結果画面に遷移すると、前のページで入力した値を保持して、画面に表示。
検索結果のISBNリンクをクリックすると、参照画面に遷移。
処理パターン2
最後に
簡易的なアプリを作成してみました。
「もっとこうした方が良い!」などのご意見があればご教示頂ければ幸いです。参考
- 投稿日:2020-05-28T20:52:39+09:00
MyBatisで忘れがちなこと個人的なまとめ
MyBatisは比較的自由にSQLが組めて便利なORマッパーなので個人的にはよく使うが、たまに使うと忘れがちなことが色々あるのでその際の備忘録としてまとめてみました。
ヘッダ明細型のデータを扱う
ヘッダテーブルと明細テーブルでが紐づいているデータを1つのマッパーでとってくるときのMyBatisの設定ファイルとSQLの書き方。
→ MyBatisでヘッダ明細型のデータを扱うページネーション用にデータをとってくるときなど行範囲を指定して取得するが、MyBatisのAPIでやろうとすると想定外の行が返ってくる、その回避方法。
→ MyBatisでヘッダ明細型のデータを扱う(行範囲を指定)そのほか
他にも順次追記していきます。
- 投稿日:2020-05-28T17:58:12+09:00
【AtCoderProblem-ABC100】のC問題「*3 or /2」をJavaで強引に突破する【コード】
はじめに
強引に突破したのでその記録を残しておきます。
問題
問題文が長いのでこちらをどうぞ
コード
TorT.javaimport java.util.Scanner; public class TorT{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int N = sc.nextInt(); int count = 0; for(int i = 0; i < N; i++){ int a = sc.nextInt(); while(true){ if(a % 2 == 0){ a = a / 2; count = count + 1; }else{ break; } } } System.out.println(count); } }aという変数が仮に偶数であれば、2で割り切れるまでfor文を回し、その回数をカウントしてあげればOK。奇数であればbreakしてfor文を抜けます。
「3をかける」という事象は「2で割り切れる」のと何ら関係ないので無視するのがいいと思います。$ java TorT > 10 >2184 >2126 >1721 >1800 >1024 >2528 >3360 >1945 >1280 >1776 39テストケースも全て「AC」で解答することができました。
- 投稿日:2020-05-28T17:30:05+09:00
minecraft1.14.4MOD開発覚え書きその1[独自レシピ]
はじめに
minecraft1.14.4のMOD開発を始めたとき、開発に関する情報の少なさに苦労したので、自分用のメモ兼これからMOD開発する方の助けになる(かもしれない)ことを書いておきます。
本記事ではMOD独自のレシピ形式を作るときの解説をします。開発環境
バージョン OS Windows10 Forge 28.2.0 JDK AdoptOpenJDK 8u242-b08 環境構築
環境構築を説明した記事は他の方が書いたものがすでにあるので、そちらを参考にしてください。
↓ 私が環境構築する際に参考にした記事です
Minecraft 1.14.4 Forge Modの作成 その1 【開発環境の準備 IntelliJ IDEA】
TNT Modders:環境構築独自レシピ
例えば二つのアイテムを同時に精錬して合金を得たい、といった通常のレシピでは実現できないことをやりたいときに必要になります。
手順
- IRecipeインターフェースを実装する
- JSONファイルからレシピを読み取るシリアライザを実装する
- シリアライザをForgeに登録する
おおまかな手順はこんな感じです。タイルエンティティなども動作させるには必要ですが、今回はレシピの読み取り・登録をメインに解説します。
実装
ほかに思いつかなかったので、上で書いた例のようなレシピを登録するのを目標とします。
それでは手順に沿って実装していきます。まずはIRecipeインターフェースを実装します。中身はこんな感じ。
IRecipe.classpublic interface IRecipe<C extends IInventory> { // インベントリ内のアイテムがレシピの材料とマッチするか boolean matches(C var1, World var2); // クラフトの結果のコピーを返す ItemStack getCraftingResult(C var1); // これだけよくわからない。基本trueを返す boolean canFit(int var1, int var2); // クラフトの結果を返す ItemStack getRecipeOutput(); default NonNullList<ItemStack> getRemainingItems(C p_179532_1_) { NonNullList<ItemStack> nonnulllist = NonNullList.withSize(p_179532_1_.getSizeInventory(), ItemStack.EMPTY); for(int i = 0; i < nonnulllist.size(); ++i) { ItemStack item = p_179532_1_.getStackInSlot(i); if (item.hasContainerItem()) { nonnulllist.set(i, item.getContainerItem()); } } return nonnulllist; } default NonNullList<Ingredient> getIngredients() { return NonNullList.create(); } default boolean isDynamic() { return false; } default String getGroup() { return ""; } default ItemStack getIcon() { return new ItemStack(Blocks.CRAFTING_TABLE); } // レシピのIDを返す ResourceLocation getId(); // 作成するレシピ形式のJSONファイルを読み書きするシリアライザを返す IRecipeSerializer<?> getSerializer(); // レシピのタイプを返す IRecipeType<?> getType(); }default以外は実装する必要があるので、
- boolean mathes(C var1, World var2);
- ItemStack getCraftingResult(C var1);
- boolean canFit(int var1, int var2);
- ItemStack getRecipeOutput();
- ResourceLocation getId();
- IRecipeSerializer<?> getSerializer();
- IRecipeType<?> getType();
の7つを実装します。実装するとだいたいこんなコードになります。
ExampleRecipe.javaimport com.google.common.collect.Lists; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.IRecipeSerializer; import net.minecraft.item.crafting.IRecipeType; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.common.util.RecipeMatcher; import java.util.List; public class ExampleRecipe implements IRecipe<IInventory> { public static final IRecipeType<ExampleRecipe> RECIPE_TYPE = new IRecipeType<ExampleRecipe>() { }; protected final ResourceLocation id; protected NonNullList<Ingredient> ingredients; protected ItemStack result; protected int cookTime; public ExampleRecipe(ResourceLocation id, NonNullList<Ingredient> ingredients, ItemStack result, int cookTime){ this.id = id; this.ingredients = ingredients; this.result = result; this.cookTime = cookTime; } // インベントリ内のアイテムがレシピの材料とマッチするか @Override public boolean matches(IInventory inventory, World world) { List<ItemStack> inputs = Lists.newArrayList(); for(int index = 0; index < inventory.getSizeInventory(); ++index){ ItemStack input = inventory.getStackInSlot(index); if(!input.isEmpty()){ inputs.add(input); } } return RecipeMatcher.findMatches(inputs, this.ingredients) != null; } // クラフトの結果のコピーを返す @Override public ItemStack getCraftingResult(IInventory inventory) { return this.result.copy(); } // これだけよくわからない。基本trueを返す @Override public boolean canFit(int i, int i1) { return true; } // クラフトの結果を返す @Override public ItemStack getRecipeOutput() { return this.result; } // レシピのIDを返す(modid:recipe_name) @Override public ResourceLocation getId() { return this.id; } // レシピのタイプを返す @Override public IRecipeType<?> getType() { return RECIPE_TYPE; } }matches()やcanFit()以外はレシピの内容を返すゲッター関数なので、難しくはないと思います。
mathes()ではRecipeMatcher.findMatches()を使うことによってインベントリのアイテムがレシピと一致するか確かめています。
canFit()がどのタイミングで使われるのかわかりませんが、バニラのコードを見た限りだとtrueを返す以外のことはやっていませんでした。また、クラフトの結果を返す関数がなぜ二つあるのかもわかっていません。こちらもバニラを参考にして、結果のコピーを返すようにしています。
ちなみにgetSerializer()を実装していないのは、まだ返すシリアライザを実装していないからです。シリアライザを実装し終えたら追加します。
import net.minecraft.item.crafting.IRecipeSerializer; import net.minecraft.util.JSONUtils; public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ExampleRecipe>{ public Serializer(){ this.setRegistryName(new ResourceLocation(modId, "serializer_name")); } private static NonNullList<Ingredient> readIngredients(JsonArray array){ NonNullList<Ingredient> ingredients = NonNullList.create(); for(JsonElement element: array){ ingredients.add(CraftingHelper.getIngredient(element)); } if(ingredients.isEmpty()){ throw new JsonParseException("No ingredients for smelting recipe"); } return ingredients; } @Override public ExampleRecipe read(ResourceLocation recipeId, JsonObject jsonObject) { ItemStack result = CraftingHelper.getItemStack(JSONUtils.getJsonObject(jsonObject, "result"), true); NonNullList<Ingredient> ingredients = readIngredients(JSONUtils.getJsonArray(jsonObject, "ingredients")); int cookTime = JSONUtils.getInt(jsonObject, "process_time", 50); return new ExampleRecipe(recipeId, ingredients, result, cookTime); } // サーバーとの通信用 @Nullable @Override public ExampleRecipe read(ResourceLocation recipeId, PacketBuffer packetBuffer) { final int index = packetBuffer.readVarInt(); NonNullList<Ingredient> ingredients = NonNullList.withSize(index, Ingredient.EMPTY); for(int col = 0; col < index; ++col){ ingredients.set(col, Ingredient.read(packetBuffer)); } ItemStack result = packetBuffer.readItemStack(); int cookTime = packetBuffer.readVarInt(); return new ExampleRecipe(recipeId, ingredients, result, cookTime); } // サーバーとの通信用 @Override public void write(PacketBuffer packetBuffer, ExampleRecipe exampleRecipe) { packetBuffer.writeVarInt(exampleRecipe.ingredients.size()); for (Ingredient ingredient : exampleRecipe.ingredients) { ingredient.write(packetBuffer); } packetBuffer.writeItemStack(exampleRecipe.result); packetBuffer.writeVarInt(exampleRecipe.cookTime); } }こちらがシリアライザのコードになります。read()が二つありますが、一つ目はJSONから、二つ目はネットワークパケットから読み込むといった違いがあります。
コンストラクタでは、このシリアライザにIDを振り分けています。このIDがJSONファイルでレシピを定義する際に指定するtypeになります。modIdとserializer_nameは適宜置き換えてください。
readIngredients()はJSONUtilsに複数のIngredientを読み取る関数がないので、自作した関数です。
注意
注意点として、読み取る際の順序が挙げられます。JSONからの読み取りは順序関係がないので気にしなくても問題ないです。しかし、パケットからの読み取りはwrite()で書き込んだのと同じ順序で行わないと、サーバーにログインする際の通信でエラーが発生します。パケットからの読み取りは先頭から順に行われるためです。
オフラインで遊ぶ分には問題ない(JSONからの読み取りしかしないため)ですが、このバグが発生しているサーバーには入れなくなります。これは致命的ですので、順序には細心の注意を払いましょう。
それでは、実装したシリアライザをExampleRecipe.javaにぶち込みたいと思います。
ExampleRecipe.javaimport com.google.common.collect.Lists; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.IRecipeSerializer; import net.minecraft.item.crafting.IRecipeType; import net.minecraft.item.crafting.Ingredient; import net.minecraft.network.PacketBuffer; import net.minecraft.util.JSONUtils; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.common.util.RecipeMatcher; import net.minecraftforge.registries.ForgeRegistryEntry; import javax.annotation.Nullable; import java.util.List; public class ExampleRecipe implements IRecipe<IInventory> { public static final IRecipeType<ExampleRecipe> public static final Serializer SERIALIZER = new Serializer(); RECIPE_TYPE = new IRecipeType<ExampleRecipe>() { }; protected final ResourceLocation id; protected NonNullList<Ingredient> ingredients; protected ItemStack result; protected int cookTime; public ExampleRecipe(ResourceLocation id, NonNullList<Ingredient> ingredients, ItemStack result, int cookTime){ this.id = id; this.ingredients = ingredients; this.result = result; this.cookTime = cookTime; } // インベントリ内のアイテムがレシピの材料とマッチするか @Override public boolean matches(IInventory inventory, World world) { List<ItemStack> inputs = Lists.newArrayList(); for(int index = 0; index < inventory.getSizeInventory(); ++index){ ItemStack input = inventory.getStackInSlot(index); if(!input.isEmpty()){ inputs.add(input); } } return RecipeMatcher.findMatches(inputs, this.ingredients) != null; } // クラフトの結果のコピーを返す @Override public ItemStack getCraftingResult(IInventory inventory) { return this.result.copy(); } // これだけよくわからない。基本trueを返す @Override public boolean canFit(int i, int i1) { return true; } // クラフトの結果を返す @Override public ItemStack getRecipeOutput() { return this.result; } // レシピのIDを返す(modid:recipe_name) @Override public ResourceLocation getId() { return this.id; } // レシピのタイプを返す @Override public IRecipeType<?> getType() { return RECIPE_TYPE; } // シリアライザを返す @Override public IRecipeSerializer<?> getSerializer() { return SERIALIZER; } public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ExampleRecipe>{ public Serializer(){ this.setRegistryName(new ResourceLocation(modId, "serializer_name")); } private static NonNullList<Ingredient> readIngredients(JsonArray array){ NonNullList<Ingredient> ingredients = NonNullList.create(); for(JsonElement element: array){ ingredients.add(CraftingHelper.getIngredient(element)); } if(ingredients.isEmpty()){ throw new JsonParseException("No ingredients for smelting recipe"); } return ingredients; } @Override public ExampleRecipe read(ResourceLocation recipeId, JsonObject jsonObject) { ItemStack result = CraftingHelper.getItemStack(JSONUtils.getJsonObject(jsonObject, "result"), true); NonNullList<Ingredient> ingredients = readIngredients(JSONUtils.getJsonArray(jsonObject, "ingredients")); int cookTime = JSONUtils.getInt(jsonObject, "process_time", 50); return new ExampleRecipe(recipeId, ingredients, result, cookTime); } @Nullable @Override public ExampleRecipe read(ResourceLocation recipeId, PacketBuffer packetBuffer) { final int index = packetBuffer.readVarInt(); NonNullList<Ingredient> ingredients = NonNullList.withSize(index, Ingredient.EMPTY); for(int col = 0; col < index; ++col){ ingredients.set(col, Ingredient.read(packetBuffer)); } ItemStack result = packetBuffer.readItemStack(); int cookTime = packetBuffer.readVarInt(); return new ExampleRecipe(recipeId, ingredients, result, cookTime); } @Override public void write(PacketBuffer packetBuffer, ExampleRecipe exampleRecipe) { packetBuffer.writeVarInt(exampleRecipe.ingredients.size()); for (Ingredient ingredient : exampleRecipe.ingredients) { ingredient.write(packetBuffer); } packetBuffer.writeItemStack(exampleRecipe.result); packetBuffer.writeVarInt(exampleRecipe.cookTime); } } }こちらがIRecipeインターフェースを継承して独自レシピを作るコードの最終形になります。ExampleRecipe.javaにgetSerializer()と、変数を追加しました。
あとはシリアライザをForgeに登録して終了です。
RegisterRecipeTypes.javaimport net.minecraft.item.crafting.IRecipeSerializer; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(modid = modId, bus = Mod.EventBusSubscriber.Bus.MOD) public class RegisterRecipeTypes { @SubscribeEvent public static void registerRecipeSerializer(RegistryEvent.Register<IRecipeSerializer<?>> event){ event.getRegistry().register(ExampleRecipe.SERIALIZER); } }レシピを定義する
最後に今回作ったレシピ形式でレシピを定義します。
example.json{ "type": "modId:serializer_name", "ingredients": [ { "item": "itemId" }, { "item": "itemId" } ] "result": "itemId" "process_time": 100 }おわりに
タイルエンティティの作成については解説するかわからないので、参考になりそうなサイトのリンクを貼って解説を終わりとさせていただきます。
↑海外の方が作成したMODチュートリアル用のリポジトリです。英語ですがコードの説明が書かれているので、見てみてください。
- 投稿日:2020-05-28T17:24:46+09:00
java でAPI呼ぶ時にjavax.net.ssl.SSLHandshakeException発生すること
だれか答えたらありがたいです
https://stackoverflow.com/q/61788988/13538484
URL url = new URL("https://xxxxRequest");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8 ");
//some headers and params
OutputStreamWriter os = new OutputStreamWriter(conn.getOutputStream());
os.write(json.toString());
os.flush();
conn.connect();
recode = conn.getResponseCode();エラーメッセージ
javax.net.ssl.SSLHandshakeException: Failed to negotiate the use of secure renegotiation
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:306)
at sun.security.ssl.ClientHandshaker.serverHello(ClientHandshaker.java:513)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:207)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
- 投稿日:2020-05-28T08:10:15+09:00
【#FizzBuzz】いろいろな言語で”1から100までの数字を数えて3の倍数と3の付く数字のときにだけアホになる”というのを書いてみる
ふだん定期的にコードを書いていないと、ふとしたときに簡単なロジックのコードも書けなくなってしまう場面に遭遇してしまったため、リハビリの意味も含めて、再勉強した際の内容を自分用のメモとしてまとめました。
0. 説明
世界のナベアツさん(桂三度さん)の最も代表的なネタ「3の倍数と3が付く数字のときだけアホになります」というものを、いろいろな言語で実装してみます。
Wikipedia - 桂三度#主な持ちネタ&ギャグ
https://ja.wikipedia.org/wiki/桂三度#主な持ちネタ&ギャグ単純に1から100までの数字を改行を入れながら出力するのですが、3の倍数と3が付く数字のときだけ「٩( ᐛ )و」という顔文字を「:(コロン)」と合わせて数字の後ろに出力します。
出力イメージ1 2 3 : ٩( ᐛ )و 4 5 6 : ٩( ᐛ )و 7 8 9 : ٩( ᐛ )و 10 11 12 : ٩( ᐛ )و 13 : ٩( ᐛ )و 14 15 : ٩( ᐛ )و 16 17 18 : ٩( ᐛ )و 19 20 21 : ٩( ᐛ )و 22 23 : ٩( ᐛ )و 24 : ٩( ᐛ )و 25 26 27 : ٩( ᐛ )و 28 29 30 : ٩( ᐛ )و 31 : ٩( ᐛ )و 32 : ٩( ᐛ )و 33 : ٩( ᐛ )و 34 : ٩( ᐛ )و 35 : ٩( ᐛ )و 36 : ٩( ᐛ )و 37 : ٩( ᐛ )و 38 : ٩( ᐛ )و 39 : ٩( ᐛ )و 40 41 42 : ٩( ᐛ )و 43 : ٩( ᐛ )و 44 45 : ٩( ᐛ )و 46 47 48 : ٩( ᐛ )و 49 50 51 : ٩( ᐛ )و 52 53 : ٩( ᐛ )و 54 : ٩( ᐛ )و 55 56 57 : ٩( ᐛ )و 58 59 60 : ٩( ᐛ )و 61 62 63 : ٩( ᐛ )و 64 65 66 : ٩( ᐛ )و 67 68 69 : ٩( ᐛ )و 70 71 72 : ٩( ᐛ )و 73 : ٩( ᐛ )و 74 75 : ٩( ᐛ )و 76 77 78 : ٩( ᐛ )و 79 80 81 : ٩( ᐛ )و 82 83 : ٩( ᐛ )و 84 : ٩( ᐛ )و 85 86 87 : ٩( ᐛ )و 88 89 90 : ٩( ᐛ )و 91 92 93 : ٩( ᐛ )و 94 95 96 : ٩( ᐛ )و 97 98 99 : ٩( ᐛ )و 1001. Bashの場合
"3の付く数字"の判定は
grep
を使えば良いので意外とシンプルに書けますBashの場合for i in $(seq 1 100) #1から100までの数字をループ do if [ $(($i % 3)) == 0 ] || [ $(echo "$i" | grep "3") ] #3の倍数と3の付く数字のときにだけアホになる then echo "${i} : ٩( ᐛ )و" else echo "$i" #それ以外は数字の出力のみ fi done2. Javaの場合
"3の付く数字"の判定は
String.valueOf(i).indexOf("3") != -1
としましたJavaの場合import java.util.*; public class Main { public static void main(String[] args) throws Exception { //1から100までの数字をループ for(int i=1; i<=100; i++){ //3の倍数と3の付く数字のときにだけアホになる if(i%3 == 0 || String.valueOf(i).indexOf("3") != -1 ){ System.out.println(i + " : ٩( ᐛ )و"); } else { System.out.println(i); } } } }3. Scalaの場合
いろいろな書き方ができると思いますが、個人的にScalaっぽいと思う書き方にしてみました
Scalaの場合object Main extends App{ println(1 to 100 map ((n) => { //1から100までの数字をキーにmapを生成 var s = n.toString() //mapの値にはキーをそのまま文字列に変換したものを代入 if(n % 3 == 0 || s.contains('3')) {s += " : ٩( ᐛ )و"} //3の倍数と3の付く数字のときにだけアホになる s }) reduceLeft ((s,e) => { s + "\n" + e }) ) //mapの値を改行しながら順に出力 }4. PowerShellの場合
PowerShellの場合もいろいろな書き方ができると思いますが、Scalaにならって関数っぽく書いてみました
PowerShellの場合1..100 | %{ if (($_ % 3 -eq 0) -or ($_ -match "3")) { echo("$_" + " : ٩( ᐛ )و") } else { echo("$_") } }5. Python3の場合
Pythonはこういう処理が非常に書きやすい言語だと思います
Python3の場合# coding: utf-8 for num in range(1, 100+1): if num % 3 == 0 or "3" in str(num): print("{0} : ٩( ᐛ )و".format(num)) else: print(num)6. Rubyの場合
RubyもPythonと同様にこういう処理が非常に書きやすい言語ですね
Rubyの場合#!/bin/env ruby for i in 1..100 if i % 3 == 0 || i.to_s =~ /3/ printf( "%d : ٩( ᐛ )و\n", i ) else printf( "%d\n", i ) end end7. Goの場合
Golangは型変換で苦労すると思いきや、意外とシンプルに書く事ができました
Goの場合package main import ( "fmt" "strconv" "strings" ) func main() { //1から100までの数字をループ for i := 1; i <= 100; i++ { //intをstringに変換 var s string = strconv.Itoa(i) //3の倍数と3の付く数字のときにだけアホになる if i%3 == 0 || strings.Contains(s, "3") { fmt.Println(s + " : ٩( ᐛ )و") } else { fmt.Println(s) } } }
以上です。
気が向いたら他の言語にも挑戦してみようと思います
- 投稿日:2020-05-28T02:06:45+09:00
【JavaServlet】千里の道も一歩から 二歩目
jspファイル作りましょう
まずはひな形から
login.jsp<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>ログイン画面</title> </head> <body> </body> </html>1行目の<%@ ~ %>はjspファイルを記述するときのお約束で、それ以外は基本的にHTMLのように扱える
ではbody内を実装していくよlogin.jsp<form action="/ServletApp/mypage" method="post"> name:<input type="text" name="name"><br> password:<input type="text" name="password"><br> <input type="submit" value="login"> </form>以下は起動時の画面※現時点では起動しても何も表示されないよ
それでは順に解説
タグ名 要素名 説明 form action submit要素をクリックしたときの遷移先
フォルダ構造にもよるが、基本的には
http://localhost:8080/プロジェクト名/action名method getとpostの2種類から指定
get・・・遷移先のURLの最後に入力したパラメータがくっつく( /action名?name=hoge&password=foo といった感じ)
post・・・くっつかない(今回使用する方)
使い分ける要点は後日記事にするinput type="text" テキストフィールド name サーバー側で入力データを取得するときのキー input type="submit" formタグ内のデータをサーバーへ送信するボタン formのactionに指定するパスですが、
プロジェクト名=ServletApp
遷移させたいjsp=mypage.jsp
の時は/ServletApp/mypage
と指定しますサーブレット作りましょう
適当にパッケージを作成したらjavaファイルを新規作成
[スーパークラス]にHttpServletと入力して[参照]をクリック
[一致する項目]からHttpServletを選択して[OK]をクリック
クラスを作成したら、[ソース]-[メソッドのオーバーライド]の順にクリック
[オーバーライドまたは実装するメソッドを選択]から、
「doGet」、「doPost」、「destroy」、「init(引数なし)」の4つを選択して[OK]をクリック
これでサーブレットで使うメソッドが追加される
メソッドの中身は消してOK
※レスポンス用の引数の命名は基本的に「res」なので、下の画像ように「resp」となっていたら「res」に直した方がいいかも
まずメソッドから解説
メソッド 説明 用途 doGet jspのformタグのmethodにgetを指定するとこのメソッドが呼びだされる 画面遷移時に実行したい主な処理 doPost 〃にpostを指定すると呼び出されるとこのメソッドが呼び出される 〃 destroy サーブレット破棄時に動く処理 ログ出力処理など init 最初に動く処理 変数の初期化処理など doGetとdoPostは利用する方だけを実装しても動くが、method="post"を指定したのにdoGetしか実装してなかった・・・といった事を防ぐため、基本的には両方宣言する
このとき、利用しない方のメソッド内にはdoXXX(req,res)
を記述する
そうすると下のような感じになる
では中身を書いていきましょう
主に使うのは下のやつ
変数 メソッド 説明 res setCharacterEncoding(String charset) jspから受け取ったパラメータを指定した文字コードに変換 〃 addCookie(Cookie cookie) クッキーを設定※後日解説 〃 sendRedirect(String URL) 指定したURLにリダイレクト(遷移) req getParameter(String key) jspのnameで指定した要素名の値を取得 〃 getSession セッションを取得(後日解説) 〃 setAttribute(String key,Object value) jspに送る要素名と値を設定 〃 getRequestDispacher(String jsppath) 送りたいjspへのファイルパスを設定 ・18行目 文字コードをUTF-8に設定する
・19、20行目 それぞれ引数にname、passwordを指定することでlogin.jspname:<input type="text" name="name"><br> password:<input type="text" name="password"><br>↑の2つのテキストフィールドから入力値を取得できる
・22行目 パスワードにgoogleを入力していたらグーグルのページへ遷移させる
・24、25行目 次のjspに渡す要素の名前と値を設定する
(25行目のように、もとのjspには無い要素でも関係なく設定できる)
・26行目 次のjspの格納パスを拡張子付きで指定する
最後にforward(req,res)を忘れずにつなげること遷移先のjsp書きましょう
mypage.jsp<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>マイページ</title> </head> <body> <%@ page import="java.util.List" %> <%@ page import="java.util.ArrayList" %> <% String name=request.getParameter("name"); List<String> list=new ArrayList<>(); list.add("Hello!"); %> <%=name%>さん、<%=list.get(0)%> </body> </html>説明のため、する必要性皆無なコードになっていますが許して
・javaのコードを書きたい ⇒ <% ~ %>で囲う
・変数の値を出力したい ⇒ <%= ~ %>で囲う
・javaのライブラリを使用したい ⇒ <%@ ~ %>で囲う
ちなみに、requestとresponseはライブラリの宣言をしなくても使えます三歩目へ続く