- 投稿日:2020-07-13T22:17:37+09:00
tcpdumpで得たパケットキャプチャをJavaで読み込む
tcpdumpでパケットキャプチャした結果を読みたいが、Wiresharkで調べるのではなく、なんらかのプログラムで読み込ませたい。pktsというJavaライブラリを使って読み込むことができたので、実施した内容をメモする。
キャプチャを行なったコマンド
TCPハンドシェイクの最初のSYNパケットとDNS応答から、通信先のドメインの一覧を取得したいので以下のようにフィルターを設定した
# 次の条件のいずれか満たすパケットを抽出する # - SYNフラグが立っている、かつ、ACKフラグが立っていない # - 送信元ポートが53(DNS) sudo tcpdump -s 0 -i en0 -nn -w tcpdump.pcap \('(tcp[tcpflags] & tcp-syn)' != 0 and '(tcp[tcpflags] & tcp-ack) ==0'\) or src port 53Javaで読み込む方法
pktsというJavaライブラリを使った。簡単な実装例の一部は以下の通り。
final Pcap pcap = Pcap.openStream("tcpdump.pcap"); pcap.loop(new PacketHandler() { @Override public boolean nextPacket(Packet packet) throws IOException { if (packet.hasProtocol(Protocol.UDP)) { System.out.println(packet.getPacket(Protocol.UDP).getPayload()); } } }IPv4やUDPの場合、パケットを取得することでそのヘッダー部分の情報を取得できる。
if (packet.hasProtocol(Protocol.IPv4)) { IPv4Packet ipv4Packet = (IPv4Packet) packet.getPacket(Protocol.IPv4); String srcIP = ipv4Packet.getSourceIP(); String dstIP = ipv4Packet.getDestinationIP(); }Payloadの内容を読み込みたい場合は、以下のような方法でバイト配列として取得できる。
UDPPacket udpPacket = (UDPPacket) packet.getPacket(Protocol.UDP); Buffer buffer = udpPacket.getPayload(); byte[] bytes = buffer.getArray();DNSのパケットとしてパースする部分は、DNSのパケットフォーマットを参考にして自前で書く必要がある。DNSの場合はドメイン部分や応答の数が可変なのでやや面倒。フォーマットを気にせずにとりあえず文字で読むことだけを目指して非常に雑に実装したものはこちら。
参考にしたリンク
- 投稿日:2020-07-13T21:53:38+09:00
Javaマスターへの道 Part1 はじめに&環境構築編
こんばんは。
今日は関東付近は秋かな?と言うくらい静かでした。過ごしやすくて勉強も捗ります。
そしてカレンダーを見るともう7月半ば。驚いている筆者です。私は今まで、Railsでのアプリケーション開発を行ってきました。
今後は就職に向けてJavaとPythonを学習する予定でしたが
「いきなり複数やってもパンクする・・・。」
と思い、Javaに専念して学習することに決めました。理由としては、
①一番使われている言語の一つであること
②Rubyよりも難易度が高いと言われているため
が挙げられます。まずは基本情報技術者試験の内容とJavaの基礎を就職までに身に付けたい!と言うことでやるぞー!
学習計画(就職までにできるだけ進める)
- ドットインストール 初めてのJava
- ドットインストール Java8 入門
- 書籍の購入 or Udemy
- 個人アプリ開発(就職が決まった場合は、仕事優先になるかと・・・!)
目標(4月までに)
- 基礎力のついたJavaエンジニアになっていること
- 10月の基本情報技術者試験に合格すること
最初は個人の日記のつもりで書きますが、1年後にはこれからJavaエンジニアを目指す方に参考になるようなシリーズになってるといいなと思います!!
環境構築
と言うわけでまずは環境構築からです。
①以下のサイトにアクセス
https://www.oracle.com/java/technologies/javase-downloads.html③MacOS Installerをクリックしその後は画面に従ってインストール完了
④以下のようなコマンドでJavaを実行できるようになります!
javac Myapp.java && java Myapp久しぶりにコードかけるから楽しみだ〜〜。
- 投稿日:2020-07-13T21:53:38+09:00
Javaエンジニアへの道 Part1 はじめに&環境構築編
こんばんは。
今日は関東付近は秋かな?と言うくらい静かでした。過ごしやすくて勉強も捗ります。
そしてカレンダーを見るともう7月半ば。驚いている筆者です。私は今まで、Railsでのアプリケーション開発を行ってきました。
今後は就職に向けてJavaとPythonを学習する予定でしたが
「いきなり複数やってもパンクする・・・。」
と思い、Javaに専念して学習することに決めました。理由としては、
①一番使われている言語の一つであること
②Rubyよりも難易度が高いと言われているため
が挙げられます。まずは基本情報技術者試験の内容とJavaの基礎を就職までに身に付けたい!と言うことでやるぞー!
学習計画(就職までにできるだけ進める)
- ドットインストール 初めてのJava
- ドットインストール Java8 入門
- 書籍の購入 or Udemy
- 個人アプリ開発(就職が決まった場合は、仕事優先になるかと・・・!)
目標(4月までに)
- 基礎力のついたJavaエンジニアになっていること
- 10月の基本情報技術者試験に合格すること
最初は個人の日記のつもりで書きますが、1年後にはこれからJavaエンジニアを目指す方に参考になるようなシリーズになってるといいなと思います!!
環境構築
と言うわけでまずは環境構築からです。
①以下のサイトにアクセス
https://www.oracle.com/java/technologies/javase-downloads.html③MacOS Installerをクリックしその後は画面に従ってインストール完了
④以下のようなコマンドでJavaを実行できるようになります!
javac Myapp.java && java Myapp久しぶりにコードかけるから楽しみだ〜〜。
- 投稿日:2020-07-13T21:29:38+09:00
Java技術者のためのC#チートシート
背景
これまでJavaをメインに仕事してきたのですが、ほとんど触れてこなかったC#を仕事で使うことになったので勉強しました。
似ているところもありますが、思想の違いや微妙な書き方の違いがあるので「これどう書くんだっけ?」が絶対に起きると思いましたので、よく使いそうなところをピックアップしてチートシートとしてまとめてみました。
細かいところは追々知っていこうと思います。使用するパッケージの宣言
Java
呼び出し方が違う。
import java.util.ArrayList;C#
using System.Collections.Generic;パッケージの定義
宣言が違う。
また思想も違うのでネーミングは注意。Java
package StudyPackage.com.anyplusC#
namespace StudyPackage // 思想が違うためドメインがないアクセス修飾子
Javaにはない
internal
やprotected internal
が存在する。
同じ修飾子でも思想の違いによることでアクセス可能範囲が異なる事にも注意。
修飾子 Java C# public どこからでもアクセス可能 どこからでもアクセス可能 protected 同一のパッケージ、または派生したクラスからアクセス可能 派生したクラスからアクセス可能 internal 存在しない 同一のアセンブリ(DLL)内でアクセス可能 protected internal 存在しない 同一のアセンブリ(DLL)内、または格納しているクラスから派生した型からのみアクセス可能 private 同じクラス内からのみアクセス可能 同じクラス内からのみアクセス可能 指定なし
(default)同一のパッケージ内でアクセスが可能 privateと同じ扱い 同一のアセンブリ(DLL)とは
同一アセンブリとは、同一のexeファイルや、DLLファイルの事。
VisualStudioのソリューション内であれば、同一プロジェクトということらしい。継承
継承するときは
:
(コロン)を使用する。
オーバーライドさせたいメソッドにはvirtual
を付け、オーバーライドする時はoverride
をつける。
virtual
とoverride
を付けないと、インスタンスが生成されて利用可能状態となる。
オーバーライドしたくない時はnew
をつける。Java
class Base { public void printStr1(){ System.out.println("Baseの1です"); } public void printStr2(){ System.out.println("Baseの2です"); } } class SubA extends Base { public final void printStr1(){ System.out.println("SubAのprintStr1です"); } public void printStr2(){ System.out.println("SubAのprintStr2です"); } }C#
class Base { public void printStr1(){ Console.WriteLine("Baseの1です"); } public virtual void printStr2(){ Console.WriteLine("Baseの2です"); } } class SubA : Base { public new void printStr1(){ Console.WriteLine("SubAのprintStr1です"); } public override void printStr2(){ Console.WriteLine("SubAのprintStr2です"); } }if文
違いなし。
Stringの比較は等値演算子でも値比較可能という違いがある。Java
String strVal = ""; if("str".equals(strVal)){ System.out.println("同じだよ"); } else { System.out.println("違うよ"); }C#
string strVal = ""; if("str" == strVal){ // 補足 Console.WriteLine("同じだよ"); } else { Console.WriteLine("違うよ"); }補足
String型を参照型なのでJavaでは等値演算子で比較すると同じインスタンスを参照しているかの比較になるが、C#の場合は裏でstring.equalsを呼び出しているためこの書き方でも値の比較が可能。
ただしstring.equalsメソッドには比較方法の指定が可能となるため、コードを書く時はstring.equalsメソッドが無難と思われる。参照→String.Equalsメソッド - .NET Tips|dobon.net
switch文
違いなし
Java
int month = 7; switch (month) { case 1: case 2: case 3: System.out.println("1Q"); break; case 4: case 5: case 6: System.out.println("2Q"); break; case 7: case 8: case 9: System.out.println("3Q"); break; case 10: case 11: case 12: System.out.println("4Q"); break; default: System.out.println("不正な値です。"); break; }C#
int month = 7; switch (month) { case 1: case 2: case 3: Console.WriteLine("1Q"); break; case 4: case 5: case 6: Console.WriteLine("2Q"); break; case 7: case 8: case 9: Console.WriteLine("3Q"); break; case 10: case 11: case 12: Console.WriteLine("4Q"); break; default: Console.WriteLine("不正な値です。"); break; }for文
Javaの拡張for文はC#ではforeachとなる。
Java
for(int i = 0; i <= 10; i++){ System.out.println(str); } String[] strArgs = {"1", "2"}; for(String str : strArgs){ System.out.println(str); }C#
for(int i = 0; i <= 10; i++){ Console.WriteLine(str); } string[] strArgs = {"1", "2"}; foreach(string str in strArgs){ Console.WriteLine(str); }while文
前判定、後判定共に違いなし。
Java
int i = 0; while (i < 5) { System.out.println(i); //0,1,2,3,4が出力される i++; } do { System.out.println(i); //0,1,2,3,4が出力される i++; } while (i < 5);C#
int i = 0; while (i < 5) { Console.WriteLine(i); //0,1,2,3,4が出力される i++; } do { Console.WriteLine(i); //0,1,2,3,4が出力される i++; } while (i < 5);参考
- 投稿日:2020-07-13T18:19:26+09:00
javaの継承について
継承
クラスの中身である変数やメソッドを、他のクラスに受け継がせること。
java, ruby, pythonなどのオブジェクト指向プログラミング言語がもつ特徴
元となるクラスを親クラス(スーパークラス・基底クラス)
受け継ぎ先のクラスを子クラス(サブクラス・派生クラス)ーーー継承のイメージーーー
Animal(親クラス)
変数
・name(名前)
・age(年齢)
メソッド
・eat(食べる)
・walk(歩く)↓↓
Dog(子クラス)
追加メソッド
・bark(吠える)
・run(走る)human(子クラス)
追加メソッド
・talk(話す)
・work(働く)ーーーーーーーーーーーー
上記イメージのように、共通の項目、性質を持った複数のクラスを作る
ときに継承を使用する。//親クラスとなる動物クラス public class Animal{ public String name; //名前 public int age; //年齢 public void eat(String something) { //食べる System.out.println(something + "を食べました"); } public void walk(int a){ //歩く System.out.println(a.toString(); + "メートル歩きました"); } } //動物クラスを継承して作った、犬クラス public class Dog extends Animal { //吠える public void bark(){ System.out.println("ワン"); } //走る public void run() { } } //動物クラスを継承して作った、人間クラス public class Human extends Animal { //しゃべる public void talk(String tk){ System.out.println("「" + tk + "」"); } //仕事をする public void work(){ } }継承を使うメリット
1、プログラミングの行数が減り、手間も省ける
継承を使わない場合、
//動物クラスを継承しないで作った、犬クラス public class Dog { public String name; //名前 public int age;//年齢 public void eat(String something) { //食べる System.out.println(something + "を食べました"); } public void walk(int a){ //歩く System.out.println(a.toString(); + "メートル歩きました"); } //吠える public void bark(){ System.out.println("ワン"); } //走る public void run() { } } //動物クラスを継承しないで作った、人間クラス public class Human extends Animal { public String name; //名前 public int age;//年齢 public void eat(String something) { //食べる System.out.println(something + "を食べました"); } public void walk(int a){ //歩く System.out.println(a.toString(); + "メートル歩きました"); } //しゃべる public void talk(String tk){ System.out.println("「" + tk + "」"); } //仕事をする public void work(){ } }親クラスの変数、メソッドを子クラス両方に重複して書いてしまう。❌
さらに、親クラスを変更しようとしたら2箇所変更しないといけない。❌
親クラスを一つにまとめていれば、変更も1箇所で良い。⭕️2、同じ継承元のクラスをまとめて扱うことができる
例えば、動物は全てトラックに乗せたい時、同じ動物というクラスを継承していればひとまとめに変数として扱うことができる
//動物クラスの型のオブジェクトを格納する配列 Animal[] animals; //犬も、人間も、まとめられる animals = { new Dog(); new Human()};継承の特徴
子クラスでは親クラスのフィールド(変数、メソッド)を使える
Animalのnameやeatはdogも持っている。
つまり、親クラスの機能は、見えないが子クラスも持っている。子クラスで親クラスのメソッドの上書き(オーバーライド)ができる
継承したメソッドを上書きできる。
これにより、同じメソッドでも違う機能を持たせることができる。
ただし、引数は揃える必要がある。継承の継承はできる(孫クラス)
子クラスを親にして、孫クラスを作成することができる。
public class Animal { } public class Dog extends Animal { } pubic class WhiteDog extends Dog { public String color = "white"; }上記は、白い犬を孫クラスとして作成
継承の注意点
親クラスにfinalを使ったフィールドは上書き禁止
finalをつけると、上書きができないメソッドになる。
勝手に処理を変更されたくない場合に使用する。2つのクラスを同時に継承することはできない
class Tuna extends Animal, Fish上記は、マグロに対し動物と魚を継承しようとしているができない❌
コンストラクタは継承されず、暗示的に実行される
※コンストラクタとは
クラスからからオブジェクトを作成した際に、自動的に実行されるメソッドのことpublic Dog extends Animal { //コンストラクタ Dog(){ } }public Dog extends Animal { //コンストラクタ Dog(){ super(); } }上と下のコードは同じ意味になる。
子クラス内でコンストラクタを上書きしようとしても、暗示的に親クラスのコンストラクタも実行される。
子クラスにコンストラクタがない場合も、親クラスのコンストラクタは実行される。
- 投稿日:2020-07-13T15:41:01+09:00
Java 関数型インターフェースについて
関数型インターフェースとは
抽象メソッドを1つだけ持つインターフェースのことで、ラムダ式やメソッド参照を渡す、代入先にすることができます。
関数型インターフェースの条件
・抽象メソッドを一つだけ持てる
・Objectクラスのpublicメソッドである抽象メソッドは含まれない
・defaultメソッドとstaticメソッドを複数持たせることができる関数型インターフェースの定義は下記のように書きます。
main.java@FunctionalInterface public interface Sample { public abstract void Hello(String name); }@FunctionalInterfaceというアノテーションをつけることで、関数型インターフェースだということを明示し、これによってインタフェースが関数型インターフェースの条件を満たしていない場合にコンパイルエラーを出すことができます。
種類
java.util.functionパッケージで、関数型インターフェースにはいくつかの種類が提供されています。受け取る引数と返す結果のパターンから大体4種類に分けられます。
基本的な関数型インターフェースは以下のとおりです。
種類 抽象メソッド できること Supplier T get() 引数がなく、T型の戻り値を返す Consumer void accept(T t) 処理を返す(値は返さない)。 Function R apply(T t) 引数としてTを受け取り、結果としてRを返す Predicate boolean test(T t) T型の引数を受け取り、booleanの値を結果として返す 他にも、引数が2つの場合のインターフェース等種類があるので、用途に応じて使い分けます。
- 投稿日:2020-07-13T13:27:03+09:00
インターフェース、java interfaceについて
インターフェースとは
コンピュータで、異なる機器・装置のあいだを接続して、交信や制御を可能にする装置やソフトウェア
ハードウェアインターフェース
ハードウェア機器同士を接続するためのコネクター(接続機)に関する形状の規格や、送受信の方法を定めたもの
(例)USBソフトウェアインターフェース
プログラム同士の間でデータのやり取りをする際の形式を定めたもの
(例)APIユーザーインターフェース(UI)
コンピューターとそれを使用する人間側を結びつける役割を指している。つまり、パソコンと人間の仲介役。
(例)「ユーザーインターフェースが使いにくい」 = 操作手順が複雑で、操作性が悪いjavaのインターフェース(interface)
Javaで使われる
interface
とは、クラスに含まれるメソッドの具体的な処理内容を記述せず、変数とメソッドの型のみを定義したものインタフェースのメンバー変数は自動的にpublic static finalが付けられるので定数になる。
<宣言>
interface インターフェース名{}<実装>
class クラス名 implements インターフェース名{}
- 投稿日:2020-07-13T13:12:31+09:00
美大生のためのプログラミング入門:四角形に関する様々な関数(その 2)
※ Qiita では、本文部分のみの印刷に苦労します。そのため、同じ内容を以下のページにも掲載しています。プリンツアウトしたり PDF 化したい人は、こちらのページを利用して下しさい:
http://gurakura.sakura.ne.jp/hellomondrian/rect3/
一覧はこちら:
http://gurakura.sakura.ne.jp/series/美大生のためのプログラミング入門/Qiita 版の総合目次:
https://qiita.com/iigura/items/37180d127da93d0b8abb不自由な四角形の描き方
さて、自由な四角形に対し、不自由な四角形の描き方を紹介してこのセクションは終わりとします。不自由な四角形の定義にも色々あるかとは思いますが、ここでは、誰が描いても同じ形となる(=相似となる)、自由度の少ない四角形としてまずは正方形を取り上げたいと思います。
正方形の描画は square 関数で行います。引数は square(x,y,l) です。x,y は正方形の左上の位置で、l が辺の長さです。
備考:ちなみに、(x,y) が示す基準とする位置は rectMode 関数で変更可能です。
background(250,250,250); size(500,500); strokeWeight(10); stroke(0,64,255); fill(255,0,0); square(100,50,200);
コラム:quad,rect,square の関係
quad 関数を使って rect 関数を表すことができます。また、rect 関数を使って square 関数を表すことができます。rect(x,y,w,h) で描かれる四角形は、(x,y), (x+w,y), (x+w,y+h), (x,y+h) という頂点で構成されます。なので、rect(x,y,w,h) は
quad(x,y, x+w,y, x+w,y+h, x,y+h)と表すことができます。同様に、square(x,y,l) も、
rect(x,y,l,l)と表すことができ、これももちろん quad 関数で表すことができます:
quad(x,y, x+l,y, x+l,y+l, x,y+l)このようにプログラミングでは、同じ表現を様々なコードで記述できます。故に、ソースコードには作者(=プログラマ)の個性が現れてきます。
プログラミングを知らない人からすると、プログラミングは無味乾燥な作業に思えるかもしれませんが、実は結構個性が現れる行為だったりします。
Comp-Position in Color α
モンドリアンの Composition in Color A をよく見てみると、青い四角形の上に重なる赤い四角形のうち、下の青い四角形との間に隙間のあるものが存在します。例えばこのような部分です:
これはこの章のはじめに挙げた図の一部を拡大したものです。このような絵はどのようにしてプログラムで実現したら良いのでしょうか?
今、我々が知っている技術は四角形を描くことと直線を描くことのみです。これらの知識のみでどうやってこのような表現を実現したら良いのでしょうか?
何も考えずに青い四角形の上に赤い四角系を描き、後から白い線で描くのでしょうか。
それとも下の青い四角形を 2 つの四角形に分割し、L字型の四角形を描画するのでしょうか(つまり、実は重ならない四角形の集合として描く)。
様々な方法が考えられると思います。
実は、この問題にも正解など存在せず、どのような方法でも皆さんが望んだ結果になれば、それが正解です。
ここでは、rect 関数で描画される四角形では輪郭線も描画できることを活用し、輪郭線により隙間を表現する方法を紹介して、この章を終わりたいと思います。なお、Composition in Color A を題材とするプログラムは後ほどまた出てきますので、今回はアルファバージョンの意味で in Color $ \alpha $ と名付けています。
// comp-position in Color alpha background(250,245,230); size(500,500); strokeWeight(10); stroke(250,240,240); // same as background fill(0,80,160); // blue rect(60,120, 200,150); fill(220,60,20); // red rect(230,70, 160,150); noStroke(); fill(80,80,80); // black rect(270,230,80,15);このように、輪郭線を輪郭線として使用しない方法もあります。また、Processing にて絵を描く場合、様々な機能が Processing には備わっており、それをどのよに用いれば、どのような効果・表現が得られるのかを良く知っている必要があります。
コラム:αバージョン、βバージョン
コンピュータの世界では、アルファバージョンとかベータバージョンなどという言葉を良く耳にします。これは一体何なのでしょうか?ソフトウェアで $\beta$ 版(ベータバージョン)というのは、ほぼ完成であるが、まだ若干直すべきところが残っているもの、というような意味あいです。
ネットワークゲーム等では、ベータテストといって、ほぼ完成しているソフトウェアで実際に遊んでもらって、バグ出しやシステム全体の調整やサーバーの性能評価を行ったりします。
ちなみに、リリース(出荷=つまり製品版)の候補となり得る程度の完成度のものは Release Candidate(リリースキャンディデート)と呼び、RC などの略号が使われます。
アルファ版(アルファバージョン)というのは、ベータの前という意味で、ほぼ完成というにはまだまだ作業が必要であるが、でもまあ、そこそこ動く、といったような意味です。
上で作った comp-position in Color も、モンドリアンの Composition in Color A と比べるとまだまだ足りない部分があります。しかし、それでもなんとなく絵のようなものは描けるので、
アルファ版であるとの意味を込めて comp-position in Color $\alpha$ としました。
- 投稿日:2020-07-13T11:46:04+09:00
【備忘録】VS CodeでJavaのビルドツール変更にハマった話
はじめに
JavaのコーディングをVS Codeでしていて、Dependencyの解決で見事にハマりました。
備忘録として(たぶん、またハマると思うんで)、調査の過程を記します。それは、興が乗った私がGitHubからクローンしたSpring Bootのサンプルコードを魔改造しようとして、調子に乗ってpom.xmlを書き換えた後に発生しました。
「追加したdependencyがVSCodeに反映されない…だと?」
そこから、調査がはじまったのです。
調査
VS Codeに導入しているJava関係の拡張機能はJava Extension Packのみですので、もしJava Extension Packの問題であれば世間様(※Google)に聞けばわかるはずです。
ところが、世間様(※Stack Overflow)のご意見を伺っても、pom.xmlを更新したら「更新を反映しますか?」的な確認ダイアログが出てきちんと反映されるよ、というご意見ばかり。
唯一可能性がありそうなのは、Java Language Serverのキャッシュに問題があって、それを消せば良いのではという意見でした。
やってみましょう。
対処1(失敗)
問題のVS Code環境は、Remote-WLSを使用してWLS2のUbuntu上で動作させています。VS Codeのキャッシュデータは、以下の場所に配置されます。
~/.vscode-server/data/User/workspaceStorage/
一旦VS Codeを終了して、Windows TerminalからUbuntuのターミナルにアクセスし、キャッシュデータを削除します。それから、VS Codeを再起動する。キャッシュの問題であればこれで問題は解決です。正直、勝ったと思いました。まあ、そう上手く行くなら我々はツールの挙動にハマることもなく、私もこんな記事を書くことはないので、そんな思いは幻想に過ぎなかったわけですが。
当然のごとく、再起動されたVS Code上で依存関係は更新されていません。
無駄に mvn compile などもしてみましたが、全くの無反応です。手詰まりか、と思った時点で、ふとあることを思い出しました。そう、pom.xmlを編集したときに表示されるはずの例のダイアログ、アレを見た記憶が一切無いのです。もしや、という思いが頭を駆け巡ります。このサンプルコードはmavenだけではなく、gradleでもビルドできるのです。自分が普段gradle派ではないので、完全に見(え)ない振りをしていたbundle.gradleが、このサンプルコードにも付属しているのです…pom.xmlと同じ階層に。
.classpath<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" output="bin/main" path="src/main/java"> <attributes> <attribute name="gradle_scope" value="main"/> <attribute name="gradle_used_by_scope" value="main,test"/> </attributes> </classpathentry> <classpathentry kind="src" output="bin/test" path="src/test/java"> <attributes> <attribute name="gradle_scope" value="test"/> <attribute name="gradle_used_by_scope" value="test"/> <attribute name="test" value="true"/> </attributes> </classpathentry> <classpathentry kind="src" output="bin/main" path="src/main/resources"> <attributes> <attribute name="gradle_scope" value="main"/> <attribute name="gradle_used_by_scope" value="main,test"/> </attributes> </classpathentry> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/> <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/> <classpathentry kind="output" path="bin/default"/> </classpath>思わず、半笑いになりました。
じつはここまで2時間弱、この問題にハマってたのです。痛恨の一撃です。このやり場のない気持ちをぶつける先がありません。
この.classpathはVS Code側で依存関係解決のために対象に含めるディレクトリやライブラリを指定するためのファイルで、自動生成されるものです。そりゃ、いくらmavenのpom.xmlを書き換えても依存関係が反映されるわけないですね。最初っから参照されてないんですから…はあ(´・ω・`)この現象を解決する手段は2択です。
・素直にgradleにビルドツールを切り替える。
・強引にmavenで依存関係が参照されるように.classpathを生成し直す。対処2(また失敗)
gradleの軍門に降るのは癪だったので、迷わず後者を選択します。
一旦VS Codeを終了し、ターミナルから.classpathとbuild.gradleをmvしました。これで再起動した時にmavenを元に.classpathが…作成されませんね?うーん?ちょっと悩みましたが、犯人は.projectでした。
.project<?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>spring-boot</name> <comment>Project complete created by Buildship.</comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>org.eclipse.buildship.core.gradleprojectbuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.buildship.core.gradleprojectnature</nature> </natures> </projectDescription>思いっきりgradleって書いてありますね。これもmvしましょう。VS Codeを再起動します。今度はきちんとmavenを元に依存関係が反映されました。
しかし、.projectと.classpathが生成されず…コーディングをするには問題ないのですが、とても気持ち悪いですね。またハマる原因にもなりかねませんし。それに、.projectと.classpathがどのタイミングで生成されるのかも気になります。検証してみましょう。
検証
別のディレクトリを作って、元にしたGitHubのリポジトリから再びcloneしてきます。それをVS Codeで開くと…生成されましたね。
こちらがVS Code起動後の状態です。この時点で既に.projectが生成され、内部はgradleを使用するように書かれていました。.classpathもあります。当然のごとくgradleを使用するようになっていました。どうやら、mavenよりgradleのほうが優先されるようです。
増えたのは、以下のファイルとディレクトリです。
・.classpath
・.gradle
・.project
・.settings
・bin
つまり、VS Codeとしてはディレクトリを開いた際に、(おそらく)build.gradleがあればそちらをビルドツールとして使用するように選択して、.projectファイルを生成し、gradleベースでプロジェクトの環境が自動的に設定される、ということなのだろうと思います。
では、このプロジェクトをもういちどcloneしなおして、今度はbuild.gradleを削除した場合の挙動を確認してみます。VS Codeを開く前のディレクトリはこんな感じです。VS Codeで開いたら、.projectも.classpathも生成されました。
.project<?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>spring-boot</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>org.eclipse.m2e.core.maven2Builder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature> </natures> </projectDescription>.classpath<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" output="target/classes" path="src/main/java"> <attributes> <attribute name="optional" value="true"/> <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> <classpathentry kind="src" output="target/test-classes" path="src/test/java"> <attributes> <attribute name="optional" value="true"/> <attribute name="maven.pomderived" value="true"/> <attribute name="test" value="true"/> </attributes> </classpathentry> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> <attributes> <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> <attributes> <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> <classpathentry kind="src" path="target/generated-sources/annotations"> <attributes> <attribute name="optional" value="true"/> <attribute name="maven.pomderived" value="true"/> <attribute name="ignore_optional_problems" value="true"/> <attribute name="m2e-apt" value="true"/> </attributes> </classpathentry> <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations"> <attributes> <attribute name="optional" value="true"/> <attribute name="maven.pomderived" value="true"/> <attribute name="ignore_optional_problems" value="true"/> <attribute name="m2e-apt" value="true"/> <attribute name="test" value="true"/> </attributes> </classpathentry> <classpathentry kind="output" path="target/classes"/> </classpath>どちらもmavenベースに置き換わっています。私の目指すところはこれなのです。
対処3(成功)
それでは、一度VS Codeでgradleプロジェクトとして認識されてしまったものを、mavenプロジェクトとして再認識させるためには、なにが足りなかったのでしょうか。
先ほどgradleプロジェクトからbuild.gradleと.classpath、.projectをmvしましたが、他にも自動生成されたディレクトリや、gradle関連のリソースが含まれています。が、怪しいのは自動生成されたリソースです。
そのため、それらをやはり全てmvしてみましょう。…あれ、変更されませんね?そこでピンと来ました。原因は.projectや.classpathをプロジェクトルート配下のbackupというディレクトリにmvしたことでした。どうも、.projectがルートディレクトリの配下になくても、サブディレクトリに存在していればそちらを読み込む、という仕様が存在するようです。
.projectと.classpathを削除することで、新しい.projectと.classpathが生成され、プロジェクトのビルドツールがmavenに切り替わりました。まとめ
- VS CodeのJava Extension Packでは、pom.xmlかbuild.gradleが存在しているディレクトリを初めて開いた際に、.projectが生成されJavaプロジェクトとして認識される。
- build.gradleとpom.xmlの両方が存在していた場合には、gradleが優先される。
- 一度ビルドツールが決定してしまった後は、VS Code側では変更する手段はない。(そのため、pom.xmlだけ修正するなどというおバカなことをやるとハマる)
- ビルドツールを移行したい場合には、build.gradle以外にも、.projectと.classpathを削除してから再度VS Codeでディレクトリを開く必要がある。
- mvとremoveは違う。
ということですね。ずいぶん遠回りしましたが、無事ビルドツールをmavenに切り替えることができました。
めでたし、めでたし。
とっぴんぱらりのぷう。
- 投稿日:2020-07-13T08:53:07+09:00
Docker × Java シンプルすぎる開発環境構築
DockerでJavaの開発環境構築
概要
Docker上にJavaのコンテナを設置するだけのシンプルな環境を構築します。
プログラムを極めし証Hello World!
をコンソールに出力するところまでをご紹介します(笑)環境
- macOS Catalina バージョン10.15.5
- Docker version 19.03.8
- docker-compose version 1.25.5
構成
最終的に以下のような構成になります。
├── docker │ └── java │ └── Dockerfile ├── docker-compose.yml └── server └── src ├── Main.class └── Main.java手順
1. docker-compose.yml作成
javaコンテナ1つだけのシンプルな構成です。
docker-compose.ymlversion: '3.6' services: java: build: ./docker/java ports: - 8080:8080 tty: true volumes: - ./server/src:/usr/src:cached2. Dockerfile作成
DockerfileFROM openjdk:11-slim RUN apt-get update WORKDIR /usr/src3. テストファイルを作成
Main.javaというテストファイルを作成します。
Main.javapublic class Main { public static void main(String[] args) { System.out.println("Hello World!"); } }4. Docker起動
// dockerビルド % docker-compose build // dockerをバックグラウンドで起動 % docker-compose up -d // 確認 % docker-compose ps Name Command State Ports ------------------------------------------------------------- java-spring_java_1 jshell Up 0.0.0.0:8080->8080/tcp5. コンパイルと実行
// インスペクション % docker-compose exec java bash // コンパイル root@5b7be900c329:/usr/src# javac Main.java // 実行 root@5b7be900c329:/usr/src# java Main Hello World!参考
- 投稿日:2020-07-13T08:41:50+09:00
AbstractFactoryパターンをenumで生成するメリット
AbstractFactoryパターンの説明には、実行時引数に文字列を渡したり、フラグでif-else判定する例をよく見かけます。
個人開発では自分がすべてを把握しているため気にならないかもしれません。
しかし、チーム開発で他チームにモジュールとして提供している場合、
Factoryの提供を受けたチームが生成可能なFactoryを知るにはどうするのか?方法としては、自分でコードを調べるか、Factory側のコード担当者に確認するかのどちらかになると思います。
Factory側の担当者は、クライアントのコード担当者の為であろうと、
自分にいちいち聞きに来た時に答えるのが面倒だという理由であろうと、
『Enumにしておけば何を生成できるかを明白に示せる』ということを知っておいてもらいたい。よく見るFactroy生成方法
クラス名を文字列で渡して判定するコード例
これには以下の問題を抱えていると考えています。
・指定する文字列が異なることは無いと言えるか?
・大文字小文字を間違わないということはあり得るか?
・そもそも、存在しないFactoryを呼び出すようなことにはならないか?フラグで生成するFactoryを判定する例
これも文字列の場合と同じ問題を抱えていると考えています。
またこの判定条件だと、0以外はすべてFactroyYが生成されるということになり、
予期せずFactoryYが生成される可能性を含んでいます。Enumを使う例
Enumを使って実装すると、生成するFactroyを明示的に示し、かつ存在しないFactoryを生成することは無くなります。
※以降のコードは、TECH SCOREのAbstractFactoryパターンを私が独自に改良したものです。
TECH SCOREとは一切関係ないことはお含みおきください。
8. AbstractFactory パターンUse.java(クライアント)
EnumであるHotpotTypeでRecipe(Factoryクラス)を指定して生成しています。
HotpotType.java(Factoryを生成するEnum)
生成可能なFactoryをここで定義しておきます。
Sukiyaki(すき焼き)を指定すれば、SukiyakiRecipeを生成するという具合です。
Recipe.java(Factoryクラス)
Factoryの生成はHotpotTypeに委譲しているため、抽象的な実装のみに専念できます。
Enumで生成するメリット
・Enumに存在するFactroyしか生成できないため、誤ったFactoryを生成することが無い。
・チーム開発の場合、生成可能なFactoryクラスがなんであるかをクライアント側のコード担当者が知っている必要がない。
Factoryクラスが不足しているなら、Factory側を担当しているプログラマに依頼すればよいだけとなる。
・Factoryの追加が簡単。
Enumに必要なFactoryクラスを追加するだけなので、if文を修正するようなことは不要となる。自分一人だけでプログラムを書いていれば、このようなことは考えなくてもよいと思うが、
チームで仕事をし、かつモジュールを分割して担当しているのであれば、
このようなに考えることで変更容易性を高め、モジュール化を円滑にできる。クラス図
Github
https://github.com/TakumiKondo/designpattern/tree/master/src/abstractfactory/hotpot
- 投稿日:2020-07-13T00:31:24+09:00
Spring Boot, Doma2, Gradleの初期設定まとめ
始めに
Eclipseで開発する際の、Spring Boot + Doma2 + Gradleの初期設定でいつもハマっているので自分用にまとめる。
環境
- IDE:Eclipse pleiades-2020-03
- Spring Boot:2.3.1 RELEASE
- Doma:2.35.0
設定手順
(省略可)Spring Initializerでアプリケーションのひな型を作成する
Doma2への依存関係を追加する
build.gradledependencies { // ... 省略 implementation 'org.seasar.doma.boot:doma-spring-boot-starter:1.4.0' annotationProcessor 'org.seasar.doma:doma-processor:2.35.0' }Eclipseの設定をする
build.gradle
のpluginsにEclipse設定用のプラグインを追加する。build.gradleplugins { // ... 省略 id 'com.diffplug.eclipse.apt' version '3.23.0' }
gradle eclipse
を実行し、設定を反映する。$ ./gradlew eclipse Starting a Gradle Daemon (subsequent builds will be faster) BUILD SUCCESSFUL in 16s 5 actionable tasks: 5 executedEclipseの設定(注釈処理)が完了していることが確認できる。
簡単なAPIを作成する
https://github.com/domaframework/doma-spring-boot/tree/1.4.0のREADMEに沿って、基本的には作成します。
Entityを定義する
Reservation.javapackage com.example.demo; import org.seasar.doma.Entity; import lombok.Data; @Data @Entity public class Reservation { private Integer id; private String name; }Dao Interfaceを定義する
ReservationDao.javapackage com.example.demo; import java.util.List; import org.seasar.doma.Dao; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.boot.ConfigAutowireable; import org.springframework.transaction.annotation.Transactional; @ConfigAutowireable @Dao public interface ReservationDao { @Select public List<Reservation> selectAll(); @Insert @Transactional public int insert(Reservation reservation); }
ReservationDao#selectAll
にカーソルを合わせ、右クリック > Doma > Jump to Sql File を押下すると、空のSQL ファイルが生成されます。(EclipseにDoma Toolsプラグインを追加している場合)※DOMA4019が発生した場合は、(補足)DOMA4019エラーが発生した場合を参照してください。
SQLファイルにクエリーを書く
selectAll.sqlSELECT id, name FROM reservation ORDER BY name ASCService, Controllerクラスを定義する
ReservationService.javapackage com.example.demo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class ReservationService { @Autowired private ReservationDao reservationDao; public List<Reservation> selectAll() { return reservationDao.selectAll(); } public int insert(Reservation reservation) { return reservationDao.insert(reservation); } }ReservationController.javapackage com.example.demo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class ReservationController { @Autowired private ReservationService reservationService; @GetMapping(path = "/") public List<Reservation> selectAll() { return reservationService.selectAll(); } @PostMapping(path = "/") public int insert(@RequestBody Reservation reservation) { return reservationService.insert(reservation); } }起動時に
Reservation
テーブルを作成するHSQLDBというJava製のインメモリDBを使います。
src/main/resources
直下にschema.sql
を配置しておくと、起動時にテーブル作成のスクリプトを流すことができます。schema.sqlCREATE TABLE reservation ( id IDENTITY, NAME VARCHAR(50) );APIの動作確認
POST(登録)
POST http://localhost:8080 HTTP/1.1 Content-Type: application/json { "id": 1, "name": "サンプルA" }レスポンス;
HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 12 Jul 2020 15:09:30 GMT Connection: close 1GET(全件取得)
GET http://localhost:8080 HTTP/1.1レスポンス;
HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 12 Jul 2020 15:10:20 GMT Connection: close [ { "id": 1, "name": "サンプルA" } ](補足)DOMA4019エラーが発生した場合
以下のように、SQLファイルの絶対パスが期待通りではない、とDOMA4019エラーが発生した場合について。
[DOMA4019] The file "META-INF/com/example/demo/ReservationDao/selectAll.sql" is not found in the classpath. The absolute path is "C:\Git\springboot-doma2-sample\bin\main\META-INF\com\example\demo\ReservationDao\selectAll.sql".対象プロジェクト上で、右クリック > プロパティ > Javaのビルドパス を修正します。
プロジェクトのデフォルト出力フォルダー ⇒ 特定の出力フォルダー(
bin/main
)に修正。終わりに
作成したアプリケーションは、リポジトリに格納しました。
build.gradle
の全量を参照したい場合などに見てください。参考