- 投稿日:2021-02-28T18:52:31+09:00
Javaのデータ型の種類と用法
はじめに
私は、現在Javaを学習しており、データ型の種類と用法についてまとめてみました。
データ型とは、変数が数値や文字列かなどを定義するためのものです。
Javaでは、変数を定義する場合、変数宣言 型 変数名; int age;のように定義しなくてはなりません。
データ型種類 用法
分類 型名 格納するデータ 変数宣言の例 整数 long 大きい整数 世界の人口 int 普通の整数 給与金額 short 小さな整数 年齢 byte さらに小さな整数 腕時計の数 少数 double 普通の少数 円周率 float 少し曖昧な少数 体重 真偽値 boolean trueかfalse 真偽 文字 char 1文字 イニシャルなど 文字列 String 文字の並び 自分の名前 型の変換
Javaには型を変換するしくみが3つあります。
①と③は自動的に行ってくれるので我々は気にする必要がありません。
①代入時の自動型変換
②強制的な型変換
③演算時の自動型変換②の強制的な型変換ですが、下記のようにします。
これをキャスト演算子と言います。
しかし、この方法は、強制的にデートの一部を失う為、よほどの理由がない限り使わない方が良いです。pubulic class Main { public static void main(String[] args){ int age = (int)25.5; #int型に変換して代入をいう命令 System.out.println(age); } } #実行結果 25最後に
JavaでもRubyみたいにアプリをつくれるようになりたいのでこれからも継続して学習を進めて行きます!
ここまで読んでいただき、ありがとうございました!
- 投稿日:2021-02-28T17:15:51+09:00
[Android Studio] DatePickerDialogの使い方(備忘録)
DatePickerDialogを使ってみた
DatePickerDialogはフラグメントの一種で、写真のような日付選択ツールが使えるようになるらしい。
フラグメントについてわからなかったので調べてみると、
フラグメントを作成するには、Fragment(またはその既存のサブクラス)のサブクラスを作成する必要があります。
(フラグメントを作成する (Android Developers))とあったので、ほほーそうかと思ってDatePickerDialogのクラスを作ってみた。
DatePickerDialogFragment.javaimport android.app.DatePickerDialog; import android.app.Dialog; import android.os.Bundle; import android.widget.DatePicker; import androidx.fragment.app.DialogFragment; import java.util.Calendar; public class DatePickerDialogFragment extends DialogFragment{ @Override public Dialog onCreateDialog(Bundle savedInstantState){ //デフォルトのタイムゾーンおよびロケールを使用してカレンダを取得 final Calendar c = Calendar.getInstance(); int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH); int day = c.get(Calendar.DAY_OF_MONTH); DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(), (DatePickerDialog.OnDateSetListener)getActivity(), year, month, day); //this はonDateSetListener return datePickerDialog; }TestDatePickerActivity.javaimport androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import java.util.Calendar; public class TestDatePickerActivity extends AppCompatActivity implements View.OnClickListener, DatePickerDialog.OnDateSetListener{ private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); textView = findViewById(R.id.textViewTest); textView.setOnClickListener(this); //今日の日付をtextViewにセット Calendar c = Calendar.getInstance(); textView.setText(String.format("%d年%d月%d日", c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH))); } @Override public void onClick(View view) { //DatePickerFragmentを表示 DatePickerDialogFragment datePicker = new DatePickerDialogFragment(); datePicker.show(getSupportFragmentManager(), "datePicker"); } //DatePickerFragment で日付がセットされたときにtextViewに取得した日付を代入する @Override public void onDateSet(DatePicker datePicker, int year, int month, int dayOfMonth) { textView.setText(String.format("%d年%d月%d日", year, month + 1, dayOfMonth)); }TestDatePickerActivity には今日の日付が出てきて、テキストをクリックすると先ほどのDatePickerDialogFragmentが呼び出されて日付選択ツールが出てくる。
ここで、DatePickerDialogFragmentクラスにOnDateSetメソッドを書いた場合、どのような記述をすれば選択した日付をtestDatePickerActivityに表示できるのかわからなかった。onDateSetメソッドをtestDatePickerActivityで継承したらなんとかできたのでよかったねというお話でした。
ちなみに…
こちらのサイトなどだと一つのクラスにまとめて書いてある。(https://codinginflow.com/tutorials/android/datepickerdialog)
サブクラスをあえて作る必要はないけれど、読みにくいのでクラスを分けた方がきれいに見えると思う。
- 投稿日:2021-02-28T16:33:57+09:00
オープンソース化したCoherence(インメモリデータグリッド)で分散キャッシュを実現しよー
Coherenceを使ってみよー
- DBのデータをメモリ上にキャッシュ
- キャッシュデータは分散
オープンソース化したCoherenceを使って上記を実現する!
メモリからデータを取得してディスクIO失くしてアプリを爆速にしよー
使ってみた感想としては、キャッシュの分散が手軽にできるし、耐障害性も簡単に実現できて
べんりーですね
情報を本投稿に細かく記載しているので誰でもできると思います!1個のjarを動かすだけで簡単に使えるので、既存のアプリに組み込んで、遅い画面だけ
Coherenceを使って、メモリにデータ持つとかもありかなーと思いました!windows機でEclipseを使ってアプリケーションを作るので、実際の開発工程がなんとなく分かるし、
発展させて、自分流のアプリケーションを作ってみるのも楽しいかもですね日本語の使い方の情報が少ないし、本があっても激古情報なので、有益なはず
なお、DBはderbyを使います
Coherenceダウンロード
今回はmavenの設定はせず、mvnのセントラルレポジトリから直接jarを仕入れます!
依存関係がないので、このjarだけで動きます、シンプル!※本格的なプロジェクト運用するなら、eclipseでmavenプロジェクトとかgradleプロジェクト作った方がよいですね
https://mvnrepository.com/artifact/com.oracle.coherence.ce/coherence/20.12
以下に配置してみた
C:\work\coherence\20.12\jarソースとREADMEをみたければ以下です
https://github.com/oracle/coherenceなんかカッコいいコミュニティサイトも立ち上がってる!
マイクロサービスでのCoherenceの使い方が紹介されてますね(英語)
https://coherence.community/Javaダウンロード
以下からダウンロード
https://www.oracle.com/java/technologies/javase-downloads.html
※オラクルアカウントの作成が必要です現在(2021年2月)githubのREADMEには、JDK 8以上をサポートと記載。
JDK15は非対応っぽいので、JDK11を使用してみる今回はEclipseを使った実装も行うので、Sierの開発機として一般的???な
windows環境に作成します以下に解凍して配置
C:\work\coherence\20.12\jdk-11.0.10_windows-x64_binPathに以下の追加も忘れずに
%JAVA_HOME%\bin動作確認
java -versionEclipseのダウンロード
以下からダウンロード
https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2020-12/R/eclipse-jee-2020-12-R-win32-x86_64.zip
※Oracle Enterprise PackのEclipseを使えると便利ですが、現在(2021年2月)、Java8までの対応っぽいし、Coherenceは商用版(~12系)までしかサポートしていなのであまり使う理由を思い当たらず。
※最近のエクリプスは、中二病っぽい名前(Luna、oxygen等)ではなく、2020-12とかになったんですね、ちょっと悲しい以下に解凍して配置
C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64eclipse.iniを編集してJDK11で動くようにしよー
※デフォルトはJava15でしたー-vm C:/work/coherence/20.12/jdk-11.0.10_windows-x64_bin/jdk-11.0.10/bin起動してみる
eclipse.exeワークスペースは以下につくりました
※デフォルトのワークスペースの接頭辞に、"eclipse-"がつくようになったんですねC:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspaceとりあえず、Javaのソースした時にフォーマッターが適用されるように設定
xmlが間違ってもいないのにエラーが出てうざいので、チェックを全外し
スペルチェックもうざいので、チェックを外し
Apache Derbyのダウンロード
JDK7までは、JDKに同梱されてたけど、JDK8以降はないので、自分でダウンロードしようー
http://db.apache.org/derby/releases/release-10_14_2_0.cgi解凍して以下に配置
C:\work\coherence\20.12\db-derby-10.14.2.0-bin起動してみますかー以下実行するだけ
startNetworkServer.batこんな感じの画面でるよ
失敗した場合は以下を確認しましょー
※私はゾンビderby?がいて、ポート競合で失敗しましたーderby.logElipseからApache Derbyに接続
表の作成
create table person (id integer primary key, name varchar(128));リフレッシュしても、繁栄されなかったので、いったんDisconnectして、再connect
※connectする際に出力されるダイアログのパスワード保存にチェック入れると次回からダイアログが出なくなるので便利PERSONテーブルできてること確認できましたー
Eclipse上にプロジェクトを作ろう
設定ファイルを作成&編集
coherence-20.12.jarを解凍しましょー
※7-zipというツールを使って私は解凍してます中に以下の三つのファイルがあります
coherence-cache-config.xml pof-config.xml tangosol-coherence-override-dev.xml上記三つのファイルをsrc配下にコピペしましょー
coherence-cache-config.xml
キャッシュの設定
私はテキストエディタでの編集が好きなので、テキストエディタをオープンこんな感じに編集しよー
<?xml version="1.0"?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <caching-scheme-mapping> <cache-mapping> <cache-name>Person</cache-name> <scheme-name>my-scheme</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <!-- partitioned caching scheme for servers --> <distributed-scheme> <scheme-name>my-scheme</scheme-name> <service-name>my-service</service-name> <serializer>pof</serializer> <backing-map-scheme> <read-write-backing-map-scheme> <internal-cache-scheme> <local-scheme /> </internal-cache-scheme> <cachestore-scheme> <class-scheme> <class-name> jp.coherence.PersonCache </class-name> </class-scheme> </cachestore-scheme> </read-write-backing-map-scheme> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> </caching-schemes> </cache-config>pof-config.xml
シリアライズの対象設定
こんな感じに編集しよー<?xml version="1.0"?> <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd"> <user-type-list> <!-- by default just include coherence POF user types --> <include>coherence-pof-config.xml</include> <user-type> <type-id>1001</type-id> <class-name>jp.coherence.Person</class-name> </user-type> </user-type-list> <enable-type-discovery>true</enable-type-discovery> </pof-config>tangosol-coherence-override-dev.xml
コヒーレンスの設定(上書き分)
まず名前を編集。以下へ
tangosol-coherence-override.xmlあとは、こんな感じに編集しよー
<?xml version="1.0"?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config coherence-operational-config.xsd"> <cluster-config> <multicast-listener> <time-to-live system-property="coherence.ttl">0</time-to-live> </multicast-listener> </cluster-config> <management-config> <managed-nodes system-property="coherence.management">all</managed-nodes> <reporter> <autostart system-property="coherence.management.report.autostart">true</autostart> </reporter> </management-config> </coherence>ビルドパスの設定
まずは、作成したxml君たちを読み込む設定をしよー
以下を指定
C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin次に、coherenceのjarを参照する設定をしよー
以下を指定
こんな感じになってればOK
※順番大切なので、xmlの読み込み先にくるようにーキャッシュのプログラミングしよー
Personクラス
xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように
ソースはこんな感じで
package jp.coherence; import java.io.IOException; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; import com.tangosol.io.pof.PortableObject; public class Person implements PortableObject, Comparable<Person> { private static final long serialVersionUID = 1L; private int id; private String name; public Person() { } public Person(String name) { this.name = name; } public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int compareTo(Person person) { return Integer.valueOf(id).compareTo(Integer.valueOf(person.getId())); } @Override public void readExternal(PofReader reader) throws IOException { this.id = reader.readInt(0); this.name = reader.readString(1); } @Override public void writeExternal(PofWriter writer) throws IOException { writer.writeInt(0, this.id); writer.writeString(1, this.name); } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } }PersonCacheクラス
xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように
ソースはこんな感じで
package jp.coherence; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.Map.Entry; import com.tangosol.net.cache.CacheStore; public class PersonCache implements CacheStore { private static ThreadLocal<Connection> tcon = new ThreadLocal<>(); private Connection getConnection() throws ClassNotFoundException, SQLException { Connection con = tcon.get(); if (con == null || con.isClosed()) { Class.forName("org.apache.derby.jdbc.ClientDriver"); con = DriverManager.getConnection("jdbc:derby://127.0.0.1:1527/sample", "test", "test"); con.setAutoCommit(false); tcon.set(con); } return con; } private Person findPerson(int personId, Connection con) throws SQLException { String sql = "SELECT * FROM PERSON WHERE ID = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, personId); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return new Person(rs.getInt("ID"), rs.getString("NAME")); } } return null; } private void storePerson(int personId, Person person, Connection con) throws SQLException { if (findPerson(personId, con) != null) { String sql = "UPDATE PERSON SET NAME = ? WHERE ID = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setString(1, person.getName()); pstmt.setInt(2, personId); pstmt.executeUpdate(); } } else { String sql = "INSERT INTO PERSON VALUES (?,?)"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, personId); pstmt.setString(2, person.getName()); pstmt.executeUpdate(); } } } @Override public Object load(Object key) { try { Connection con = getConnection(); int personId = Integer.parseInt(key.toString()); return findPerson(personId, con); } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void erase(Object key) { int id = Integer.parseInt(key.toString()); String sql = "DELETE FROM PERSON WHERE ID = ?"; try { Connection con = getConnection(); try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, id); pstmt.executeUpdate(); con.commit(); } catch (SQLException ex) { con.rollback(); } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void store(Object key, Object value) { Person person = (Person) value; int personId = Integer.parseInt(key.toString()); try { Connection con = getConnection(); try { storePerson(personId, person, con); con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void storeAll(Map entries) { Map<Integer, Person> persons = (Map<Integer, Person>) entries; try { Connection con = getConnection(); try { for (Entry<Integer, Person> entry : persons.entrySet()) { int playerId = entry.getKey(); Person player = entry.getValue(); storePerson(playerId, player, con); } con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } }クライアントのプログラミングして行こー
ここからは適当な名前でつくってOK
ClientGet
データ取得用のクラス
キャッシュにあれば、キャッシュからデータとるし、
キャッシュになければ、データベースからデータ取得するよソースはこんな感じで
package jp.coherence; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class ClientGet { public static void main(String[] args) { NamedCache person = CacheFactory.getCache("Person"); System.out.println("size: " + person.size()); System.out.println(person.get(1)); System.out.println(person.get(2)); System.out.println(person.get(3)); System.out.println(person.get(4)); System.out.println(person.get(5)); System.out.println("size: " + person.size()); } }ClientPut
データ挿入する用のクラス
ソースはこんな感じで
package jp.coherence; import java.util.Iterator; import java.util.Map; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class ClientPut { public static void main(String[] args) { NamedCache person = CacheFactory.getCache("Person"); person.put(1, new Person(1, "aaaa")); person.put(2, new Person(2, "bbbb")); person.put(3, new Person(3, "cccc")); person.put(4, new Person(4, "dddd")); Iterator<Map.Entry<Integer, Person>> pi = person.entrySet().iterator(); System.out.println("size:" + person.size()); while (pi.hasNext()) { Map.Entry entry = pi.next(); Person p = (Person) entry.getValue(); System.out.println(p.toString()); } } }さあ、動作確認だー①
まずは、キャッシュ用のサーバーを立ち上げよう
コマンドプロンプトで以下を入力してねset memory=1g set java_opts=-Xms%memory% -Xmx%memory% java -server -showversion %java_opts% -cp "C:\work\coherence\20.12\db-derby-10.14.2.0-bin\lib\derbyclient.jar;C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin;C:\work\coherence\20.12\jar\coherence-20.12.jar" com.tangosol.net.DefaultCacheServer %*クラスパスに以下が指定してあるよー
- C:\work\coherence\20.12\db-derby-10.14.2.0-bin\lib\derbyclient.jar
- C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin
- C:\work\coherence\20.12\jar\coherence-20.12.jar"
分散してデータを持ちたいので、2台たちあげよー
※カレントディレクトリにレポートが出力される設定になっています
同じディレクトリで立ち上げると、レポート内容が重複するので、
2台は別々のディレクトリで起動しよー
なお、設定の変更は可能ですちゃんとスタートしたようですね
さあ、動作確認だー②
データ(キャッシュ&DBへ)を入れまーす
※derby立ち上げておいてねClientPutを実行
以下が表示されれば成功!
DBにデータが入ったか確認
DBにデータ入っているので、キャッシュ上にデータがあるか、それからデータが2台で分散してるか確認
jconsole(jdkに同梱されてるよ)で確認しよー
コマンドプロンプトで以下を実行jconsoleあとで1号機を落とすので、2号機を選択
※情報はどっちでもみれるよー1号機には、3つデータ入ってる
2号機には、1つデータ入ってる
データあるし、一応分散できてるね
次に1号機落としてみよー(コマンドプロンプト上でctrl+cとかで停止させてください)
2号機にデータうつるはずお、成功しましたねー
アプリからもゲット(ClientGetを実行)してみよー
どうやらうまくいっているようですね
あとは各自色々なことして遊んでみてください
- 投稿日:2021-02-28T16:33:57+09:00
オープンソース化したCoherence(インメモリデータグリッド)のアプリケーションを作ってみよー
Coherenceの使い方
オープンソース化したCoherenceを使ってみる!
実際に使いそうな、DBからデータ取得して、メモリにキャッシュする流れをやってみよー
windows機でEclipseを使ってアプリケーションを作るので、実際の開発工程がなんとなく分かるし、
発展させて、自分流のアプリケーションを作ってみるのも楽しいかもですね日本語の使い方の情報が少ないし、本があっても激古情報なので、有益なはず
なお、DBはderbyを使います
Coherenceダウンロード
mavenの設定するのめんどいので、mvnのセントラルレポジトリから直接jarを仕入れます!
依存関係がないので、このjarだけで動きます、意外とシンプル!※本格的なプロジェクト運用するなら、eclipseでmavenプロジェクトとかgradleプロジェクト作った方がよいですね
https://mvnrepository.com/artifact/com.oracle.coherence.ce/coherence/20.12
以下に配置してみた
C:\work\coherence\20.12\jarソースとREADMEをみたければ以下です
https://github.com/oracle/coherenceなんかカッコいいコミュニティサイトも立ち上がってる!
マイクロサービスでのCoherenceの使い方が紹介されてますね(英語)
https://coherence.community/Javaダウンロード
以下から好きなバージョン選択してダウンロード
https://www.oracle.com/java/technologies/javase-downloads.html
※めんどうですが、オラクルアカウントの作成が必要ですgithubのREADMEには、JDK 8以上をサポートと記載。
JDK15は非対応っぽいので、JDK11を使用してみる今回はEclipseを使った実装も行うので、Sierの開発機として一般的???な
windows環境に作成します以下に解凍して配置
C:\work\coherence\20.12\jdk-11.0.10_windows-x64_binPathに以下の追加も忘れずに
%JAVA_HOME%\bin動作確認
java -versionEclipseのダウンロード
以下からダウンロード
https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2020-12/R/eclipse-jee-2020-12-R-win32-x86_64.zip
※Oracle Enterprise PackのEclipseを使えると便利ですが、現在(2021年2月)、Java8までの対応っぽいし、Coherenceは商用版(~12系)までしかサポートしていなのであまり使う理由を思い当たらず。
※最近のエクリプスは、中二病っぽい名前(Luna、oxygen等)ではなく、2020-12とかになったんですね、ちょっと悲しい以下に解凍して配置
C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64eclipse.iniを編集してJDK11で動くようにしよー
※デフォルトはJava15でしたー-vm C:/work/coherence/20.12/jdk-11.0.10_windows-x64_bin/jdk-11.0.10/bin起動してみる
eclipse.exeワークスペースは以下につくりました
※デフォルトのワークスペースの接頭辞に、"eclipse-"がつくようになったんですねC:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspaceとりあえず、Javaのソースした時にフォーマッターが適用されるように設定
xmlが間違ってもいないのにエラーが出てうざいので、チェックを全外し
スペルチェックもうざいので、チェックを外し
Apache Derbyのダウンロード
JDK7までは、JDKに同梱されてたけど、JDK8以降はないので、自分でダウンロードしようー
http://db.apache.org/derby/releases/release-10_14_2_0.cgi解凍して以下に配置
C:\work\coherence\20.12\db-derby-10.14.2.0-bin起動してみますかー以下実行するだけ
startNetworkServer.batこんな感じの画面でるよ
失敗した場合は以下を確認しましょー
※私はゾンビderby?がいて、ポート競合で失敗しましたーderby.logElipseからApache Derbyに接続
表の作成
create table person (id integer primary key, name varchar(128));リフレッシュしても、繁栄されなかったので、いったんDisconnectして、再connect
※connectする際に出力されるダイアログのパスワード保存にチェック入れると次回からダイアログが出なくなるので便利PERSONテーブルできてること確認できましたー
Eclipse上にプロジェクトを作ろう
設定ファイルを作成&編集
coherence-20.12.jarを解凍しましょー
※7-zipというツールを使って私は解凍してます中に以下の三つのファイルがあります
coherence-cache-config.xml pof-config.xml tangosol-coherence-override-dev.xml上記三つのファイルをsrc配下にコピペしましょー
coherence-cache-config.xml
キャッシュの設定
私はテキストエディタでの編集が好きなので、テキストエディタをオープンこんな感じに編集しよー
<?xml version="1.0"?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <caching-scheme-mapping> <cache-mapping> <cache-name>Person</cache-name> <scheme-name>my-scheme</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <!-- partitioned caching scheme for servers --> <distributed-scheme> <scheme-name>my-scheme</scheme-name> <service-name>my-service</service-name> <serializer>pof</serializer> <backing-map-scheme> <read-write-backing-map-scheme> <internal-cache-scheme> <local-scheme /> </internal-cache-scheme> <cachestore-scheme> <class-scheme> <class-name> jp.coherence.PersonCache </class-name> </class-scheme> </cachestore-scheme> </read-write-backing-map-scheme> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> </caching-schemes> </cache-config>pof-config.xml
シリアライズの対象設定
こんな感じに編集しよー<?xml version="1.0"?> <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd"> <user-type-list> <!-- by default just include coherence POF user types --> <include>coherence-pof-config.xml</include> <user-type> <type-id>1001</type-id> <class-name>jp.coherence.Person</class-name> </user-type> </user-type-list> <enable-type-discovery>true</enable-type-discovery> </pof-config>tangosol-coherence-override-dev.xml
コヒーレンスの設定(上書き分)
まず名前を編集。以下へ
tangosol-coherence-override.xmlあとは、こんな感じに編集しよー
<?xml version="1.0"?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config coherence-operational-config.xsd"> <cluster-config> <multicast-listener> <time-to-live system-property="coherence.ttl">0</time-to-live> </multicast-listener> </cluster-config> <management-config> <managed-nodes system-property="coherence.management">all</managed-nodes> <reporter> <autostart system-property="coherence.management.report.autostart">true</autostart> </reporter> </management-config> </coherence>ビルドパスの設定
まずは、作成したxml君たちを読み込む設定をしよー
以下を指定
C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin次に、coherenceのjarを参照する設定をしよー
以下を指定
こんな感じになってればOK
キャッシュのプログラミングしよー
Personクラス
xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように
ソースはこんな感じで
package jp.coherence; import java.io.IOException; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; import com.tangosol.io.pof.PortableObject; public class Person implements PortableObject, Comparable<Person> { private static final long serialVersionUID = 1L; private int id; private String name; public Person() { } public Person(String name) { this.name = name; } public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int compareTo(Person person) { return Integer.valueOf(id).compareTo(Integer.valueOf(person.getId())); } @Override public void readExternal(PofReader reader) throws IOException { this.id = reader.readInt(0); this.name = reader.readString(1); } @Override public void writeExternal(PofWriter writer) throws IOException { writer.writeInt(0, this.id); writer.writeString(1, this.name); } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } }PersonCacheクラス
xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように
ソースはこんな感じで
package jp.coherence; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.Map.Entry; import com.tangosol.net.cache.CacheStore; public class PersonCache implements CacheStore { private static ThreadLocal<Connection> tcon = new ThreadLocal<>(); private Connection getConnection() throws ClassNotFoundException, SQLException { Connection con = tcon.get(); if (con == null || con.isClosed()) { Class.forName("org.apache.derby.jdbc.ClientDriver"); con = DriverManager.getConnection("jdbc:derby://127.0.0.1:1527/sample", "test", "test"); con.setAutoCommit(false); tcon.set(con); } return con; } private Person findPerson(int personId, Connection con) throws SQLException { String sql = "SELECT * FROM PERSON WHERE ID = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, personId); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return new Person(rs.getInt("ID"), rs.getString("NAME")); } } return null; } private void storePerson(int personId, Person person, Connection con) throws SQLException { if (findPerson(personId, con) != null) { String sql = "UPDATE PERSON SET NAME = ? WHERE ID = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setString(1, person.getName()); pstmt.setInt(2, personId); pstmt.executeUpdate(); } } else { String sql = "INSERT INTO PERSON VALUES (?,?)"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, personId); pstmt.setString(2, person.getName()); pstmt.executeUpdate(); } } } @Override public Object load(Object key) { try { Connection con = getConnection(); int personId = Integer.parseInt(key.toString()); return findPerson(personId, con); } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void erase(Object key) { int id = Integer.parseInt(key.toString()); String sql = "DELETE FROM PERSON WHERE ID = ?"; try { Connection con = getConnection(); try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, id); pstmt.executeUpdate(); con.commit(); } catch (SQLException ex) { con.rollback(); } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void store(Object key, Object value) { Person person = (Person) value; int personId = Integer.parseInt(key.toString()); try { Connection con = getConnection(); try { storePerson(personId, person, con); con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void storeAll(Map entries) { Map<Integer, Person> persons = (Map<Integer, Person>) entries; try { Connection con = getConnection(); try { for (Entry<Integer, Person> entry : persons.entrySet()) { int playerId = entry.getKey(); Person player = entry.getValue(); storePerson(playerId, player, con); } con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } }クライアントのプログラミングして行こー
ここからは適当な名前でつくってOK
ClientGet
データ取得用のクラス
キャッシュにあれば、キャッシュからデータとるし、
キャッシュになければ、データベースからデータ取得するよソースはこんな感じで
package jp.coherence; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class ClientGet { public static void main(String[] args) { NamedCache person = CacheFactory.getCache("Person"); System.out.println("size: " + person.size()); System.out.println(person.get(1)); System.out.println(person.get(2)); System.out.println(person.get(3)); System.out.println(person.get(4)); System.out.println(person.get(5)); System.out.println("size: " + person.size()); } }ClientPut
データ挿入する用のクラス
ソースはこんな感じで
package jp.coherence; import java.util.Iterator; import java.util.Map; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class ClientPut { public static void main(String[] args) { NamedCache person = CacheFactory.getCache("Person"); person.put(1, new Person(1, "aaaa")); person.put(2, new Person(2, "bbbb")); person.put(3, new Person(3, "cccc")); person.put(4, new Person(4, "dddd")); Iterator<Map.Entry<Integer, Person>> pi = person.entrySet().iterator(); System.out.println("size:" + person.size()); while (pi.hasNext()) { Map.Entry entry = pi.next(); Person p = (Person) entry.getValue(); System.out.println(p.toString()); } } }さあ、動作確認だー①
まずは、キャッシュ用のサーバーを立ち上げよう
コマンドプロンプトで以下を入力してねset memory=1g set java_opts=-Xms%memory% -Xmx%memory% java -server -showversion %java_opts% -cp "C:\work\coherence\20.12\db-derby-10.14.2.0-bin\lib\derbyclient.jar;C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin;C:\work\coherence\20.12\jar\coherence-20.12.jar" com.tangosol.net.DefaultCacheServer %*クラスパスに以下が指定してあるよー
- C:\work\coherence\20.12\db-derby-10.14.2.0-bin\lib\derbyclient.jar
- C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin
- C:\work\coherence\20.12\jar\coherence-20.12.jar"
分散してデータを持ちたいので、2台たちあげよー
ちゃんとスタートしたようですね
さあ、動作確認だー②
データ(キャッシュ&DBへ)を入れまーす
※derby立ち上げておいてねClientPutを実行
以下が表示されれば成功!
DBにデータが入ったか確認
DBにデータ入っているので、キャッシュ上にデータがあるか、それからデータが2台で分散してるか確認
jconsole(jdkに同梱されてるよ)で確認しよー
コマンドプロンプトで以下を実行jconsoleあとで1号機を落とすので、2号機を選択
※情報はどっちでもみれるよー1号機には、3つデータ入ってる
2号機には、1つデータ入ってる
データあるし、一応分散できてるね
次に1号機落としてみよー(コマンドプロンプト上でctrl+cとかで停止させてください)
2号機にデータうつるはずお、成功しましたねー
アプリからもゲット(ClientGetを実行)してみよー
どうやらうまくいっているようですね
あとは各自色々なことして遊んでみてください
- 投稿日:2021-02-28T16:33:57+09:00
オープンソース化したCoherence(インメモリデータグリッド)のEclipse利用での使い方
Coherenceを使ってみよー
- DBのデータをメモリ上にキャッシュ
- キャッシュデータは分散
オープンソース化したCoherenceを使って上記を実現する!
メモリからデータを取得してディスクIO失くしてアプリを爆速にしよー
使ってみた感想としては、キャッシュの分散が手軽にできるし、耐障害性も簡単に実現できて
べんりーですね
情報を本投稿に細かく記載しているので誰でもできると思います!1個のjarを動かすだけで簡単に使えるので、既存のアプリに組み込んで、遅い画面だけ
Coherenceを使って、メモリにデータ持つとかもありかなーと思いました!windows機でEclipseを使ってアプリケーションを作るので、実際の開発工程がなんとなく分かるし、
発展させて、自分流のアプリケーションを作ってみるのも楽しいかもですね日本語の使い方の情報が少ないし、本があっても激古情報なので、有益なはず
なお、DBはderbyを使います
Coherenceダウンロード
今回はmavenの設定はせず、mvnのセントラルレポジトリから直接jarを仕入れます!
依存関係がないので、このjarだけで動きます、シンプル!※本格的なプロジェクト運用するなら、eclipseでmavenプロジェクトとかgradleプロジェクト作った方がよいですね
https://mvnrepository.com/artifact/com.oracle.coherence.ce/coherence/20.12
以下に配置してみた
C:\work\coherence\20.12\jarソースとREADMEをみたければ以下です
https://github.com/oracle/coherenceなんかカッコいいコミュニティサイトも立ち上がってる!
マイクロサービスでのCoherenceの使い方が紹介されてますね(英語)
https://coherence.community/Javaダウンロード
以下からダウンロード
https://www.oracle.com/java/technologies/javase-downloads.html
※オラクルアカウントの作成が必要です現在(2021年2月)githubのREADMEには、JDK 8以上をサポートと記載。
JDK15は非対応っぽいので、JDK11を使用してみる今回はEclipseを使った実装も行うので、Sierの開発機として一般的???な
windows環境に作成します以下に解凍して配置
C:\work\coherence\20.12\jdk-11.0.10_windows-x64_binPathに以下の追加も忘れずに
%JAVA_HOME%\bin動作確認
java -versionEclipseのダウンロード
以下からダウンロード
https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2020-12/R/eclipse-jee-2020-12-R-win32-x86_64.zip
※Oracle Enterprise PackのEclipseを使えると便利ですが、現在(2021年2月)、Java8までの対応っぽいし、Coherenceは商用版(~12系)までしかサポートしていなのであまり使う理由を思い当たらず。
※最近のエクリプスは、中二病っぽい名前(Luna、oxygen等)ではなく、2020-12とかになったんですね、ちょっと悲しい以下に解凍して配置
C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64eclipse.iniを編集してJDK11で動くようにしよー
※デフォルトはJava15でしたー-vm C:/work/coherence/20.12/jdk-11.0.10_windows-x64_bin/jdk-11.0.10/bin起動してみる
eclipse.exeワークスペースは以下につくりました
※デフォルトのワークスペースの接頭辞に、"eclipse-"がつくようになったんですねC:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspaceとりあえず、Javaのソースした時にフォーマッターが適用されるように設定
xmlが間違ってもいないのにエラーが出てうざいので、チェックを全外し
スペルチェックもうざいので、チェックを外し
Apache Derbyのダウンロード
JDK7までは、JDKに同梱されてたけど、JDK8以降はないので、自分でダウンロードしようー
http://db.apache.org/derby/releases/release-10_14_2_0.cgi解凍して以下に配置
C:\work\coherence\20.12\db-derby-10.14.2.0-bin起動してみますかー以下実行するだけ
startNetworkServer.batこんな感じの画面でるよ
失敗した場合は以下を確認しましょー
※私はゾンビderby?がいて、ポート競合で失敗しましたーderby.logElipseからApache Derbyに接続
表の作成
create table person (id integer primary key, name varchar(128));リフレッシュしても、繁栄されなかったので、いったんDisconnectして、再connect
※connectする際に出力されるダイアログのパスワード保存にチェック入れると次回からダイアログが出なくなるので便利PERSONテーブルできてること確認できましたー
Eclipse上にプロジェクトを作ろう
設定ファイルを作成&編集
coherence-20.12.jarを解凍しましょー
※7-zipというツールを使って私は解凍してます中に以下の三つのファイルがあります
coherence-cache-config.xml pof-config.xml tangosol-coherence-override-dev.xml上記三つのファイルをsrc配下にコピペしましょー
coherence-cache-config.xml
キャッシュの設定
私はテキストエディタでの編集が好きなので、テキストエディタをオープンこんな感じに編集しよー
<?xml version="1.0"?> <cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd"> <caching-scheme-mapping> <cache-mapping> <cache-name>Person</cache-name> <scheme-name>my-scheme</scheme-name> </cache-mapping> </caching-scheme-mapping> <caching-schemes> <!-- partitioned caching scheme for servers --> <distributed-scheme> <scheme-name>my-scheme</scheme-name> <service-name>my-service</service-name> <serializer>pof</serializer> <backing-map-scheme> <read-write-backing-map-scheme> <internal-cache-scheme> <local-scheme /> </internal-cache-scheme> <cachestore-scheme> <class-scheme> <class-name> jp.coherence.PersonCache </class-name> </class-scheme> </cachestore-scheme> </read-write-backing-map-scheme> </backing-map-scheme> <autostart>true</autostart> </distributed-scheme> </caching-schemes> </cache-config>pof-config.xml
シリアライズの対象設定
こんな感じに編集しよー<?xml version="1.0"?> <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd"> <user-type-list> <!-- by default just include coherence POF user types --> <include>coherence-pof-config.xml</include> <user-type> <type-id>1001</type-id> <class-name>jp.coherence.Person</class-name> </user-type> </user-type-list> <enable-type-discovery>true</enable-type-discovery> </pof-config>tangosol-coherence-override-dev.xml
コヒーレンスの設定(上書き分)
まず名前を編集。以下へ
tangosol-coherence-override.xmlあとは、こんな感じに編集しよー
<?xml version="1.0"?> <coherence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config coherence-operational-config.xsd"> <cluster-config> <multicast-listener> <time-to-live system-property="coherence.ttl">0</time-to-live> </multicast-listener> </cluster-config> <management-config> <managed-nodes system-property="coherence.management">all</managed-nodes> <reporter> <autostart system-property="coherence.management.report.autostart">true</autostart> </reporter> </management-config> </coherence>ビルドパスの設定
まずは、作成したxml君たちを読み込む設定をしよー
以下を指定
C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin次に、coherenceのjarを参照する設定をしよー
以下を指定
こんな感じになってればOK
※順番大切なので、xmlの読み込み先にくるようにーキャッシュのプログラミングしよー
Personクラス
xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように
ソースはこんな感じで
package jp.coherence; import java.io.IOException; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; import com.tangosol.io.pof.PortableObject; public class Person implements PortableObject, Comparable<Person> { private static final long serialVersionUID = 1L; private int id; private String name; public Person() { } public Person(String name) { this.name = name; } public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int compareTo(Person person) { return Integer.valueOf(id).compareTo(Integer.valueOf(person.getId())); } @Override public void readExternal(PofReader reader) throws IOException { this.id = reader.readInt(0); this.name = reader.readString(1); } @Override public void writeExternal(PofWriter writer) throws IOException { writer.writeInt(0, this.id); writer.writeString(1, this.name); } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } }PersonCacheクラス
xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように
ソースはこんな感じで
package jp.coherence; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.Map.Entry; import com.tangosol.net.cache.CacheStore; public class PersonCache implements CacheStore { private static ThreadLocal<Connection> tcon = new ThreadLocal<>(); private Connection getConnection() throws ClassNotFoundException, SQLException { Connection con = tcon.get(); if (con == null || con.isClosed()) { Class.forName("org.apache.derby.jdbc.ClientDriver"); con = DriverManager.getConnection("jdbc:derby://127.0.0.1:1527/sample", "test", "test"); con.setAutoCommit(false); tcon.set(con); } return con; } private Person findPerson(int personId, Connection con) throws SQLException { String sql = "SELECT * FROM PERSON WHERE ID = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, personId); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return new Person(rs.getInt("ID"), rs.getString("NAME")); } } return null; } private void storePerson(int personId, Person person, Connection con) throws SQLException { if (findPerson(personId, con) != null) { String sql = "UPDATE PERSON SET NAME = ? WHERE ID = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setString(1, person.getName()); pstmt.setInt(2, personId); pstmt.executeUpdate(); } } else { String sql = "INSERT INTO PERSON VALUES (?,?)"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, personId); pstmt.setString(2, person.getName()); pstmt.executeUpdate(); } } } @Override public Object load(Object key) { try { Connection con = getConnection(); int personId = Integer.parseInt(key.toString()); return findPerson(personId, con); } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void erase(Object key) { int id = Integer.parseInt(key.toString()); String sql = "DELETE FROM PERSON WHERE ID = ?"; try { Connection con = getConnection(); try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, id); pstmt.executeUpdate(); con.commit(); } catch (SQLException ex) { con.rollback(); } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void store(Object key, Object value) { Person person = (Person) value; int personId = Integer.parseInt(key.toString()); try { Connection con = getConnection(); try { storePerson(personId, person, con); con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } @Override public void storeAll(Map entries) { Map<Integer, Person> persons = (Map<Integer, Person>) entries; try { Connection con = getConnection(); try { for (Entry<Integer, Person> entry : persons.entrySet()) { int playerId = entry.getKey(); Person player = entry.getValue(); storePerson(playerId, player, con); } con.commit(); } catch (SQLException ex) { con.rollback(); throw ex; } } catch (SQLException | ClassNotFoundException ex) { throw new RuntimeException(ex); } } }クライアントのプログラミングして行こー
ここからは適当な名前でつくってOK
ClientGet
データ取得用のクラス
キャッシュにあれば、キャッシュからデータとるし、
キャッシュになければ、データベースからデータ取得するよソースはこんな感じで
package jp.coherence; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class ClientGet { public static void main(String[] args) { NamedCache person = CacheFactory.getCache("Person"); System.out.println("size: " + person.size()); System.out.println(person.get(1)); System.out.println(person.get(2)); System.out.println(person.get(3)); System.out.println(person.get(4)); System.out.println(person.get(5)); System.out.println("size: " + person.size()); } }ClientPut
データ挿入する用のクラス
ソースはこんな感じで
package jp.coherence; import java.util.Iterator; import java.util.Map; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; public class ClientPut { public static void main(String[] args) { NamedCache person = CacheFactory.getCache("Person"); person.put(1, new Person(1, "aaaa")); person.put(2, new Person(2, "bbbb")); person.put(3, new Person(3, "cccc")); person.put(4, new Person(4, "dddd")); Iterator<Map.Entry<Integer, Person>> pi = person.entrySet().iterator(); System.out.println("size:" + person.size()); while (pi.hasNext()) { Map.Entry entry = pi.next(); Person p = (Person) entry.getValue(); System.out.println(p.toString()); } } }さあ、動作確認だー①
まずは、キャッシュ用のサーバーを立ち上げよう
コマンドプロンプトで以下を入力してねset memory=1g set java_opts=-Xms%memory% -Xmx%memory% java -server -showversion %java_opts% -cp "C:\work\coherence\20.12\db-derby-10.14.2.0-bin\lib\derbyclient.jar;C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin;C:\work\coherence\20.12\jar\coherence-20.12.jar" com.tangosol.net.DefaultCacheServer %*クラスパスに以下が指定してあるよー
- C:\work\coherence\20.12\db-derby-10.14.2.0-bin\lib\derbyclient.jar
- C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin
- C:\work\coherence\20.12\jar\coherence-20.12.jar"
分散してデータを持ちたいので、2台たちあげよー
ちゃんとスタートしたようですね
さあ、動作確認だー②
データ(キャッシュ&DBへ)を入れまーす
※derby立ち上げておいてねClientPutを実行
以下が表示されれば成功!
DBにデータが入ったか確認
DBにデータ入っているので、キャッシュ上にデータがあるか、それからデータが2台で分散してるか確認
jconsole(jdkに同梱されてるよ)で確認しよー
コマンドプロンプトで以下を実行jconsoleあとで1号機を落とすので、2号機を選択
※情報はどっちでもみれるよー1号機には、3つデータ入ってる
2号機には、1つデータ入ってる
データあるし、一応分散できてるね
次に1号機落としてみよー(コマンドプロンプト上でctrl+cとかで停止させてください)
2号機にデータうつるはずお、成功しましたねー
アプリからもゲット(ClientGetを実行)してみよー
どうやらうまくいっているようですね
あとは各自色々なことして遊んでみてください
- 投稿日:2021-02-28T15:21:43+09:00
【Java】Listの重複を削除する方法
1. Setクラスを使う方法
以下では、HashSetクラスに数値が重複する List を渡し、重複なしのリストに詰め替えています。
//数値が重複するListを作成 List<Integer> listWithDuplicates = Arrays.asList(0, 1, 2, 3, 4, 4, 5, 5); //HashSetに数値が重複するListを渡し、重複なしのリストに詰め替える List<Integer> listWithoutDuplicates = new ArrayList<>(new HashSet<>(listWithDuplicates)); System.out.println(listWithoutDuplicates); // 出力 -> [0, 1, 2, 3, 4, 5]2. Streamのdistinct メソッドを使う方法
以下では、Streamのdistinctメソッドで重複を削除し、新たなListに詰め替えています。
//数値が重複するListを作成 List<Integer> listWithDuplicates = Arrays.asList(0, 1, 2, 3, 4, 4, 5, 5); //distinct()で重複を削除し、重複なしのリストに詰め替える List<Integer> listWithoutDuplicates = listWithDuplicates.stream() .distinct() .collect(Collectors.toList()); System.out.println(listWithoutDuplicates); // 出力 -> [0, 1, 2, 3, 4, 5]参考
HowToDoInJava : Java Stream distinct()
HowToDoInJava : How to remove duplicate elements in ArrayList
- 投稿日:2021-02-28T13:14:47+09:00
Keycloak
Keycloakとは
Keycloak(キークローク)とはWeb上でのSSO(シングルサインオン)を実現するためのJavaベースの認証ソフトウェアです。初版は2014年9月にリリースされ、最新版は2021年2月にリリースされた12.0.3になります。また、2016年にRed HatはRH SSO製品を、PicketLinkフレームワークからKeycloak upstreamプロジェクトをベースとしたものに切り替え、それに伴い、PicketLinkのコードベースがKeycloakにマージされました。現在、シングルサインオやAPIアクセスの認証・認可制御を実現するソフトウェアの分野において、最も勢いのあるソフトウェアの1つと言われています。
機能
Keycloakの主な機能は以下の通りになります。(ドキュメントから抜粋)
・ソーシャルログイン(Google,GitHub,Facebook,Twitterなど)
・同じRealmに含まれる全てのアプリケーションに対する、シングルサインオン/サインオフ
・二要素認証(Google AuthenticatorやFreeOTPを使用したTOTP/HOTPのサポート)
・ユーザーフェデレーション(LDAPやActive Directory)
・ケルベロス連携(サーバーにログイン済のユーザーに対する認証連携)
・ユーザーに自分たちのアカウントを一元管理することを許可するためのアカウント管理コンソール用語
(ドキュメントから抜粋)
用語 説明 認証 ユーザーを特定し、検証するプロセス 認可 ユーザーに対してアクセスを許可するプロセス ロール ロールは、ユーザーのタイプまたはカテゴリーを識別する。
Admin,User,Manager,Employeeが組織内に存在する典型的なロールとなる。ユーザー・ロール・マッピング ユーザー・ロール・マッピングはロールとユーザーの間のマッピングを定義する。
ユーザーには、0以上のロールを関連付けることができる。
このロールマッピング情報を、トークンとアサーションにカプセル化して、アプリケーションが管理する様々なリソースに対するアクセス許可を決定できるようにする。複合ロール 複合ロールは、他のロールと関連付けることができるロール。 グループ グループはユーザーのグループを管理する。
グループには、属性を定義することができ、ロールもグループにマップすることができる。
グループのメンバーになったユーザーは、そのグループが定義する属性とロールマッピングを継承する。レルム レルムは、ユーザー、クレデンシャル、ロール、及び、グループのセットを管理する。
ユーザーは属しているレルムにログインする。レルムは互いに分離されており、制御するユーザーのみを管理して、認証することができる。クライアント クライアントは、keycloakにユーザーの認証を要求できるエンティティー。 クライアントアダプター クライアントアダプターは、keycloakによる通信とセキュリティー保護を可能にするためにアプリケーション環境にインストールするプラグイン。
keycloakには、異なるプラットフォーム向けのいくつかのアダプターが用意されており、ダウンロードが可能。クライアントロール クライアントは、特定のロールを定義できる。
これは、基本的にクライアント専用のロールの名前空間IDトークン ユーザーに関する識別情報を提供するトークン。
これは、OpenID Connect仕様の一部アクセストークン サービスへのアクセスを許可するHTTPリクエストの一部として提供できるトークン。
これは、OpenID Connect及びOAuth 2.0仕様の一部アサーション ユーザーに関する情報 サービス・アカウント 各クライアントには、アクセストークンを取得するための組み込みサービス・アカウントがある ダイレクト・グラント クライアントがREST呼び出しを介して、ユーザーに変わってアクセストークンを取得する方法。 プロトコル・マッパー 各クライアントに対し、どのクレーム及びアサーションをOIDCトークンまたはSAMLアサーションに格納するかを調整できる。
クライアントごとに、プロトコル・マッパーを構成する起動
手順は以下になります。
1.keycloakのダウンロード
2.サーバーの起動
3.管理者ユーザーの作成
4.管理コンソールにログインKeycloakを起動するためには以下の条件が必要になります。
・Javaを実行可能なOS
・Java 8 JDK
・RAM512M以上
・1G以上のディスクスペースまた、起動する前に環境変数
JAVA_HOME
を設定していることを確認しましょう。
設定方法はこちらダウンロードしたら適当なディレクトリに解答し、
bin
ディレクトリにあるstandalone.sh
またはstandalone.bat
を実行することでKeycloakサーバーが起動します。・Linux/Unix
$ ./bin/standalone.sh・Windows
> bin\standalone.batサーバーを起動したら
http://localhost:8080/auth
のURLにアクセスすることでこの管理者ユーザーの作成ができます。
画面左に位置する
Administration Console
に管理者の名前、パスワードを入力することで管理コンソール画面に移ります。
左側がメニューになり、レルムの設定、ユーザーなどを作成することができます。
初めに作られるMasterレルム
はレルムの階層で最上位のレルムになります。このレルムの管理者アカウントには、サーバー・インスタンスで作成された他のレルムを表示及び管理する権限があります。参考
https://keycloak-documentation.openstandia.jp/master/ja_JP/server_admin/index.html#%E6%A6%82%E8%A6%81
https://qiita.com/daian183/items/30f01e162e03567ff21b
https://www.javadrive.jp/start/install/index4.html
- 投稿日:2021-02-28T04:49:11+09:00
SharedPreferencesを自前で難読化するのはもう古い?これからはEncryptedSharedPrefenrecesを使おう
EncryptedSharedPreferencesとは
Android Developersのサイトにある通り、「SharedPreferencesのKeyとValueを暗号化する実装」を提供するものです。
(Android Developers公式サイトより引用)
An implementation of SharedPreferences that encrypts keys and values.
今までのgetSharedPreferences()などで使用していたAPIの使い勝手はそのままに、暗号化する機能を提供してくれるので、手軽にセキュリティを高めることができます。
動作条件
・プロジェクトのminSdkVersionが23以上であること
これはandroidx.securityのライブラリのminSdkVersionが「23」に指定されているためです。
(APIレベル23未満では、EncryptedSharedPreferencesを使用するためのマスターキー情報がないので使用できない)ライブラリの組み込み
build.gradleに以下のコードを追加します。
ライブラリのバージョンはその時の状況に応じて、適宜変更してください。dependencies { implementation "androidx.security:security-crypto:1.1.0-alpha03" }EncryptedSharedPreferencesを使ってみる
MainActivity.ktimport android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey class MainActivity : AppCompatActivity() { companion object { const val PREF_NAME = "encrypted_prefs" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val prefs = EncryptedSharedPreferences.create( applicationContext, PREF_NAME, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) with (prefs.edit()) { putString("foo", "bar") apply() } val savedValue = prefs.getString("foo", "Nothing") Toast.makeText(this, """the saved value of the key "foo" is $savedValue""", Toast.LENGTH_LONG).show() } }上記のコードでアプリをビルドすると、EncryptedSharedPreferencesを使用して保存したデータ("bar"という文字列)がToastで表示されるはずです。
非常にお手軽に使えますね!以下、上記のコードについて軽く触れておきます。
MasterKey
MasterKeyはandroidx.securityライブラリで使われるマスターキーをラップするクラスです。
このキー情報を使用して、EncryptedSharedPreferencesのKey/Valueの値を暗号化します。
また、このキーはAndroid KeyStoreに格納されるため安全に使用することができます。
以下のコードで、デフォルト設定のマスターキーを取得します。val mainKey = MasterKey.Builder(applicationContext) //master keyに使用するKeySchemeを指定. 本記事執筆時、AES256_GCMのみ定義されている .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build()※開発者自身で、鍵生成のパラメータを指定することができますが、上記の(デフォルト)設定の使用が推奨されているようです。
EncryptedSharedPreferences.create
以下のコードで、EncryptedSharedPreferencesインスタンスを生成することができます。
val prefs = EncryptedSharedPreferences.create( applicationContext, PREF_NAME, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM )それぞれの引数について。
- 第1引数 … Contextインスタンス。SharedPreferencesインスタンス生成のために使用
- 第2引数 … 保存先のファイル名
- 第3引数 … MasterKeyインスタンス
- 第4引数 … keyを暗号化するために参照されるPrefKeyEncryptionScheme情報。本記事執筆時、AES256_SIVのみが定義されている
- 第5引数 … valueを暗号化するために参照されるPrefValueEncryptionScheme情報。本記事執筆時、AES256_GCMのみが定義されている
保存されたデータを見てみよう
今回は保存するファイル名を「encrypted_prefs」としました。
/data/data/${アプリのパッケージ名}/shared_prefs/encrypted_prefs.xmlの中身を見てみましょう。
筆者の環境では以下のようになっていました。暗号化された文字列の値は環境により異なります。encrypted_prefs.xml(EncryptedSharedPreferencesを使用した場合)<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a9010d7073b7e3c32bd1ce2184835ef4b9582676b80a4a8158f4cff8f8f3f852cd3191216e3cbb97aaa6ade6e49f8f8e58570dcec6dda3144ec2abfbdca143553576c2e60f13f6398f496540421768693f3ea9f1be169983e03c37dec39f1302364b0f710dbdeaea5c495efb15f26e8aab33a0fb882a8ae0f500238206a78776479a08b78f45a766b3fe06c2047c0479c70715bb7e99989a4feef0d74223552b6e5c292a5c06532efd3a661a4408f3bfa1a006123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b6579100118f3bfa1a0062001</string> <string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">128601101a6c6904bfc93b66d78e2f65ad240a53a2d203536980b93d5f85b74e695b94ca27333264d4d42f3c69b4549a773a876c44c7afe99345402ab376dd896501b297f2e84c00fb1c9842e094dc77ac9012fb4e543a4180d991c80b01077e8483cc8e1097b35ec8f1a2e92e9557ccc4f1c60891a1549b0ae42a1bb4f1601346b377dda1087dc56f1a4208e580f910123b0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b6579100118e580f9102001</string> <string name="AWQIX/Ot9q4upiyy+U2fP4RhilkKf/vT">AQIeQGVdBmATM9ubBcCayGfwulNZzpZbQJdADMe97l6cNJJ1XrPjoHa4vP0=</string> </map>"foo"というKeyが"AWQIX/Ot9q4upiyy+U2fP4RhilkKf/vT"という文字列に、
"bar"というValueが"AQIeQGVdBmATM9ubBcCayGfwulNZzpZbQJdADMe97l6cNJJ1XrPjoHa4vP0="という文字列に暗号化されています。
ユーザーにとって意味不明な文字列になっていますね。アプリの秘匿情報を使うのが楽になりました!"__androidx_security_crypto_encrypted_prefs_key_keyset__"と"__androidx_security_crypto_encrypted_prefs_value_keyset__"は、それぞれKeyとValueの暗号化に使用するkeyset情報で、自動的に保存される情報のようです。
※ちなみに、EncryptedSharedPreferencesではなく、getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)を使ってデータを保存した場合は、以下のようになります。
encrypted_prefs.xml(getSharedPreferencesを使用した場合)<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="foo">bar</string> </map>補足
プロジェクトのminSdkVersionが23未満の場合
プロジェクトのminSdkVersionが23未満の時は、以下の修正を加えればビルド自体はできます。
・AndroidManifest.xmlにoverrideLibraryの設定を追加AndroidManifes.xml<!--以下のコードを追加--> <uses-sdk tools:overrideLibrary="androidx.security"/>・端末のOSバージョンによって処理を分岐
if (Build.VERSION.SDK_INT >= 23) { APIレベルが23以上の時のみ、処理を実施 }※ただし、後述のように、このような実装を行うメリットはあまりないでしょう。
最後に
EncryptedSharedPreferencesは、今までのgetSharedPreferences()による共有設定の編集と同じようなAPIで、KeyValuePairデータを端末に安全に保存できる
プロジェクトのminSdkVersionが23以上の新規開発では、getSharedPreferences()ではなく、EncryptedSharedPreferencesの使用を積極的に検討しよう
プロジェクトのminSdkVersionを23以上にあげられない場合、素直に既存のSharedPreferencesを使い回す(必要に応じて、KeyやValueを自前で暗号化)なり、他の暗号化ライブラリの使用を検討した方が良いでしょう。
=> SharedPreferencesを使って保存される情報の暗号化有無がOSバージョンによって変わるのは好ましくない。暗号化するなら、全ての端末を対象とするのが基本。また、OSバージョンにより暗号化の実装を変えるのは無駄に複雑。すでにリリースされているアプリで、EncryptedSharedPreferences以外によるデータ保存処理を行なっている場合、EncryptedSharedPreferencesに移行すべきかどうかは、アプリの特性やプロジェクトの状況に応じて様々なので、それぞれで考えよう
- 投稿日:2021-02-28T02:13:18+09:00
【Java】classファイルからクラスの情報を取得してみる
概要
Javaはビルドを行うとclassファイルを出力します。このclassファイルの中身をあえて解析しようという人は、あまりいないかもしれません。がしかし、ソースコードの分析とかに役立つかもということで、今回は試しにclassファイルの情報を取得してみたので、その方法を紹介してみたいと思います。
今回取得する情報
java.lang.Class
で取得できるものを取得します。こちらの記事で紹介されているようにフィールド名やメソッド名など、クラスに関する基本的な情報は一通り取得できます。javap
で取得できるものを取得します。javapではクラスファイルはすべからくjavapしようの記事で紹介されている通り、ファイルサイズやチェックサム、コンパイルしたバージョンやコンスタントプールなどが参照できます。コンスタントプールについてはこちらの記事が詳しいです。コンスタントプールでは、呼出元から何のクラス・メソッドが、呼ばれているかの情報が取得できたりします。処理の流れと参考記事
- classファイルが出力されるパスと、取得したいクラス情報のパッケージを指定して、その配下のパッケージのclassファイルのパスを取得します。ディレクトリ情報を取得する実装は【Java入門】ディレクトリ内のファイルを再帰的に取得する方法の記事が参考になります。
- 取得したclassファイルのパスから
ClassLoader
を使用して、java.lang.Class
を取得します。 ClassLoaderからの読み込みについては、実行時にクラスファイルを読み込むクラスローダーの記事が参考になります。- Runtimeクラスを使って
javap
を実行し結果を取得します。結果の解析については割愛し、今回は純粋に文字列として結果取得出来るまでとします。Runtimeクラスの詳細については外部プロセスの起動(Runtime#exec)が参考になります。実装サンプル
【実行クラス】
Executer.javapublic class Executer { public static void getClassInfo(String classFilePath, String packageName) { try { // ディレクトリとパッケージを指定しクラスファイルのパスを取得 List<String> classFilePaths = DirectoryService.getClassFilePathList(classFilePath, packageName); // クラスファイルからjava.lang.Classの情報を取得 ClassLoaderService classLoaderService = new ClassLoaderService(); List<Class> classes = classFilePaths.stream().map(path -> { try { return classLoaderService.getClassFromFilePath(path); } catch (Exception e) { return null; } }).filter(c -> c != null).collect(Collectors.toList()); // クラスファイルからjavapの情報を取得 List<String> javapResults = classFilePaths.stream().map(path -> { try { return classLoaderService.getJavap(path); } catch (Exception e) { return null; } }).filter(c -> c != null).collect(Collectors.toList()); } catch (Exception e) { e.printStackTrace(); } } }【classファイルのパス一覧取得】
DirectoryService.javapublic class DirectoryService { public static List<String> getClassFilePathList(String targetPath, String packageName) throws Exception { // パッケージ名をパス形式に変換 String packageDirectory = packageName.replaceAll("\\.", "/"); try(Stream<Path> stream = Files.walk(Paths.get(targetPath))) { return stream.filter(path -> { String pathString = path.toString(); // パス配下かつクラスファイル return pathString.contains(packageDirectory) && pathString.endsWith(".class"); }).map(path -> path.toString()).collect(Collectors.toList()); } catch(Exception e) { throw e; } } }【java.lang.Classの結果取得】
ClassLoaderService.javapublic class ClassLoaderService extends ClassLoader { private static final int BUF_SIZE = 1024; public Class getClassFromFilePath(String targetPath) throws Exception { FileInputStream in = new FileInputStream(targetPath); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buf = new byte[BUF_SIZE]; int len = in.read(buf); while(len != -1) { out.write(buf, 0, len); len = in.read(buf); } byte[] loadedData = out.toByteArray(); Class loadedCLass = defineClass(null, loadedData, 0, loadedData.length); return loadedCLass; } }【javapの結果取得】
ClassLoaderService.javapublic class ClassLoaderService extends ClassLoader { public String getJavap(String targetPath) throws Exception { ProcessBuilder pb = new ProcessBuilder( "javap", "-v", targetPath.replaceAll("\\.class", "") ); Process p = pb.start(); InputStream stdIn = p.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stdIn)); InputStream errIn = p.getErrorStream(); BufferedReader errReader = new BufferedReader(new InputStreamReader(errIn)); String result = ""; try { String str = reader.readLine(); while(str != null){ result += str; str = reader.readLine(); } str = errReader.readLine(); while(str != null){ result += str; str = errReader.readLine(); } int ret = p.waitFor(); if (ret != 0) { throw new IOException("ret faliled"); } } finally { reader.close(); errReader.close(); stdIn.close(); errIn.close(); } return result; } }
- 投稿日:2021-02-28T00:29:14+09:00
【Javaについてまとめてみたオブジェクト編】インターフェースについて
はじめに
今回はJavaのインターフェースについて書いていきたいと思います。
抽象クラスとの違いなど理解するのが難しい印象です。
少しでも理解につながれば幸いです。
抽象クラスについては下記の記事で書いているのでぜひご覧ください。
【Javaについてまとめてみたオブジェクト編】抽象クラスについてインターフェースとは
まず、インターフェースは抽象クラスと似ていますが、クラスではありません。
javaの参照型の一つであり、抽象クラスを親クラスで使いた時は「継承」と言いますが、インターフェースは「実装」と言います。
特徴としては、
- クラス宣言時にinterfaceを付ける
- newをしてインスタンスを作成することが出来ない。
- メソッドは抽象メソッドしか作成できない。
- アクセス修飾子はpublicかpackageしか使えない。
- 抽象クラスは一つしか継承できないが、インターフェースは複数実装できる。
といったものがあげられます。
インターフェースの宣言
インターフェースの宣言public interface インターフェース名{};宣言する時はこのようにinterfaceを付けます。
インターフェースの実装
インターフェースの実装//1つ実装 public Class クラス名 implements インターフェース名{}; //2つ実装 public Class クラス名 implements インターフェース名,インターフェース名2{}; //継承と併用 public Class 親クラス名 extends 子クラス名 implements インターフェース名,インターフェース名2{};実装するときはこのようにimplementsを付けます。
複数インターフェースを実装したい場合はカンマ区切りで記述してあげます。
継承と併用することも可能です!抽象クラスとの違いを考えてみた
ここまで見た感じ抽象メソッドしか書けない点や複数の実装が出来る点など抽象クラスとの違いがいくつかありましたが、正直使い方のニュアンスが今いち分からんと思われる方もいるかと思います。
色々調べて他の方のQiitaも見たところ、このような違いが書かれていました。クラス仕様としての型定義をしたいならインタフェース
継承関係にあり、処理の再利用をしたいなら抽象クラス引用元:【詳解】抽象クラスとインタフェースを使いこなそう!!
一旦、人間で例えましょう。
人間には日本人、アメリカ人など様々な人種がいます。
これらの人種はみんなしゃべることが出来ますが、言語が違います。
仮に親クラス「人間」を作るとしたら抽象メソッドで「言語」みたいなのを作れば、それぞれサブクラスで話し方の違いを表現できます。
これが抽象クラスの役割で、必ずあるような機能を再利用してますね。では、特技について考えてみます。
必ずしも人間みんなが特技があるという訳ではないと思うので、抽象メソッドで必ず実装するような設計にはできないかと思います。
ですが、特技を実装したくなった時に特技のインターフェースを作っておけば、いつでも個別の人間ごとにプラスで処理が書けるイメージになります。これがインターフェースです。さいごに
インターフェースどうだったでしょうか。
正直、私も完全に理解できてるか怪しいので指摘があれば教えてください!
途中で引用させていただいた記事は理解しやすいように思えたのでおススメします。ご覧いただきありがとうございました。