20210228のJavaに関する記事は10件です。

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みたいにアプリをつくれるようになりたいのでこれからも継続して学習を進めて行きます!

ここまで読んでいただき、ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android Studio] DatePickerDialogの使い方(備忘録)

DatePickerDialogを使ってみた

DatePickerDialogはフラグメントの一種で、写真のような日付選択ツールが使えるようになるらしい。
スクリーンショット 2021-02-28_DatePickerDialog.png

フラグメントについてわからなかったので調べてみると、

フラグメントを作成するには、Fragment(またはその既存のサブクラス)のサブクラスを作成する必要があります。
(フラグメントを作成する (Android Developers))

とあったので、ほほーそうかと思ってDatePickerDialogのクラスを作ってみた。

DatePickerDialogFragment.java
import 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.java
import 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が呼び出されて日付選択ツールが出てくる。
スクリーンショット 2021-02-28 165208.png

ここで、DatePickerDialogFragmentクラスにOnDateSetメソッドを書いた場合、どのような記述をすれば選択した日付をtestDatePickerActivityに表示できるのかわからなかった。onDateSetメソッドをtestDatePickerActivityで継承したらなんとかできたのでよかったねというお話でした。

ちなみに…

こちらのサイトなどだと一つのクラスにまとめて書いてある。(https://codinginflow.com/tutorials/android/datepickerdialog)
サブクラスをあえて作る必要はないけれど、読みにくいのでクラスを分けた方がきれいに見えると思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オープンソース化したCoherence(インメモリデータグリッド)で分散キャッシュを実現しよー

Coherenceを使ってみよー

image.png

  • 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
image.png

以下に配置してみた

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環境に作成します

image.png

以下に解凍して配置

C:\work\coherence\20.12\jdk-11.0.10_windows-x64_bin

ユーザー変数設定
image.png

Pathに以下の追加も忘れずに

%JAVA_HOME%\bin

動作確認

java -version

以下のような画面でればOK
image.png

Eclipseのダウンロード

以下からダウンロード
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_64

eclipse.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のソースした時にフォーマッターが適用されるように設定

image.png

xmlが間違ってもいないのにエラーが出てうざいので、チェックを全外し

image.png

スペルチェックもうざいので、チェックを外し

image.png

Apache Derbyのダウンロード

JDK7までは、JDKに同梱されてたけど、JDK8以降はないので、自分でダウンロードしようー
http://db.apache.org/derby/releases/release-10_14_2_0.cgi

image.png

解凍して以下に配置

C:\work\coherence\20.12\db-derby-10.14.2.0-bin

起動してみますかー以下実行するだけ

startNetworkServer.bat

こんな感じの画面でるよ

image.png

失敗した場合は以下を確認しましょー
※私はゾンビderby?がいて、ポート競合で失敗しましたー

derby.log

ElipseからApache Derbyに接続

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

表の作成

image.png

create table person (id integer primary key, name varchar(128));

image.png

リフレッシュしても、繁栄されなかったので、いったんDisconnectして、再connect
※connectする際に出力されるダイアログのパスワード保存にチェック入れると次回からダイアログが出なくなるので便利

image.png

image.png

PERSONテーブルできてること確認できましたー

image.png

Eclipse上にプロジェクトを作ろう

image.png

image.png

image.png

image.png

設定ファイルを作成&編集

coherence-20.12.jarを解凍しましょー
※7-zipというツールを使って私は解凍してます

image.png

中に以下の三つのファイルがあります

coherence-cache-config.xml
pof-config.xml
tangosol-coherence-override-dev.xml

上記三つのファイルをsrc配下にコピペしましょー

image.png

image.png

coherence-cache-config.xml

キャッシュの設定
私はテキストエディタでの編集が好きなので、テキストエディタをオープン

image.png

こんな感じに編集しよー

<?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>

ビルドパスの設定

image.png

まずは、作成したxml君たちを読み込む設定をしよー

image.png

以下を指定

C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin

次に、coherenceのjarを参照する設定をしよー

image.png

以下を指定

image.png

こんな感じになってればOK
※順番大切なので、xmlの読み込み先にくるようにー

image.png

キャッシュのプログラミングしよー

Personクラス

xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように

image.png

image.png

ソースはこんな感じで

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記載してあるので、パッケージ名とクラス名の変更はしないように

image.png

ソースはこんな感じで

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台は別々のディレクトリで起動しよー
 なお、設定の変更は可能です

ちゃんとスタートしたようですね

image.png

さあ、動作確認だー②

データ(キャッシュ&DBへ)を入れまーす
※derby立ち上げておいてね

ClientPutを実行

image.png

以下が表示されれば成功!

image.png

DBにデータが入ったか確認

image.png

image.png

DBにデータ入っているので、キャッシュ上にデータがあるか、それからデータが2台で分散してるか確認

jconsole(jdkに同梱されてるよ)で確認しよー
コマンドプロンプトで以下を実行

jconsole

あとで1号機を落とすので、2号機を選択
※情報はどっちでもみれるよー

image.png

1号機には、3つデータ入ってる

image.png

2号機には、1つデータ入ってる

image.png

データあるし、一応分散できてるね

次に1号機落としてみよー(コマンドプロンプト上でctrl+cとかで停止させてください)
2号機にデータうつるはず

image.png

お、成功しましたねー

アプリからもゲット(ClientGetを実行)してみよー

image.png

どうやらうまくいっているようですね

あとは各自色々なことして遊んでみてください

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オープンソース化したCoherence(インメモリデータグリッド)のアプリケーションを作ってみよー

Coherenceの使い方

オープンソース化したCoherenceを使ってみる!
実際に使いそうな、DBからデータ取得して、メモリにキャッシュする流れをやってみよー
windows機でEclipseを使ってアプリケーションを作るので、実際の開発工程がなんとなく分かるし、
発展させて、自分流のアプリケーションを作ってみるのも楽しいかもですね

image.png

日本語の使い方の情報が少ないし、本があっても激古情報なので、有益なはず

なお、DBはderbyを使います

Coherenceダウンロード

mavenの設定するのめんどいので、mvnのセントラルレポジトリから直接jarを仕入れます!
依存関係がないので、このjarだけで動きます、意外とシンプル!

※本格的なプロジェクト運用するなら、eclipseでmavenプロジェクトとかgradleプロジェクト作った方がよいですね

https://mvnrepository.com/artifact/com.oracle.coherence.ce/coherence/20.12
image.png

以下に配置してみた

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環境に作成します

image.png

以下に解凍して配置

C:\work\coherence\20.12\jdk-11.0.10_windows-x64_bin

ユーザー変数設定
image.png

Pathに以下の追加も忘れずに

%JAVA_HOME%\bin

動作確認

java -version

以下のような画面でればOK
image.png

Eclipseのダウンロード

以下からダウンロード
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_64

eclipse.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のソースした時にフォーマッターが適用されるように設定

image.png

xmlが間違ってもいないのにエラーが出てうざいので、チェックを全外し

image.png

スペルチェックもうざいので、チェックを外し

image.png

Apache Derbyのダウンロード

JDK7までは、JDKに同梱されてたけど、JDK8以降はないので、自分でダウンロードしようー
http://db.apache.org/derby/releases/release-10_14_2_0.cgi

image.png

解凍して以下に配置

C:\work\coherence\20.12\db-derby-10.14.2.0-bin

起動してみますかー以下実行するだけ

startNetworkServer.bat

こんな感じの画面でるよ

image.png

失敗した場合は以下を確認しましょー
※私はゾンビderby?がいて、ポート競合で失敗しましたー

derby.log

ElipseからApache Derbyに接続

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

表の作成

image.png

create table person (id integer primary key, name varchar(128));

image.png

リフレッシュしても、繁栄されなかったので、いったんDisconnectして、再connect
※connectする際に出力されるダイアログのパスワード保存にチェック入れると次回からダイアログが出なくなるので便利

image.png

image.png

PERSONテーブルできてること確認できましたー

image.png

Eclipse上にプロジェクトを作ろう

image.png

image.png

image.png

image.png

設定ファイルを作成&編集

coherence-20.12.jarを解凍しましょー
※7-zipというツールを使って私は解凍してます

image.png

中に以下の三つのファイルがあります

coherence-cache-config.xml
pof-config.xml
tangosol-coherence-override-dev.xml

上記三つのファイルをsrc配下にコピペしましょー

image.png

image.png

coherence-cache-config.xml

キャッシュの設定
私はテキストエディタでの編集が好きなので、テキストエディタをオープン

image.png

こんな感じに編集しよー

<?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>

ビルドパスの設定

image.png

まずは、作成したxml君たちを読み込む設定をしよー

image.png

以下を指定

C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin

次に、coherenceのjarを参照する設定をしよー

image.png

以下を指定

image.png

こんな感じになってればOK

image.png

キャッシュのプログラミングしよー

Personクラス

xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように

image.png

image.png

ソースはこんな感じで

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記載してあるので、パッケージ名とクラス名の変更はしないように

image.png

ソースはこんな感じで

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台たちあげよー

ちゃんとスタートしたようですね

image.png

さあ、動作確認だー②

データ(キャッシュ&DBへ)を入れまーす
※derby立ち上げておいてね

ClientPutを実行

image.png

以下が表示されれば成功!

image.png

DBにデータが入ったか確認

image.png

image.png

DBにデータ入っているので、キャッシュ上にデータがあるか、それからデータが2台で分散してるか確認

jconsole(jdkに同梱されてるよ)で確認しよー
コマンドプロンプトで以下を実行

jconsole

あとで1号機を落とすので、2号機を選択
※情報はどっちでもみれるよー

image.png

1号機には、3つデータ入ってる

image.png

2号機には、1つデータ入ってる

image.png

データあるし、一応分散できてるね

次に1号機落としてみよー(コマンドプロンプト上でctrl+cとかで停止させてください)
2号機にデータうつるはず

image.png

お、成功しましたねー

アプリからもゲット(ClientGetを実行)してみよー

image.png

どうやらうまくいっているようですね

あとは各自色々なことして遊んでみてください

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オープンソース化したCoherence(インメモリデータグリッド)のEclipse利用での使い方

Coherenceを使ってみよー

image.png

  • 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
image.png

以下に配置してみた

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環境に作成します

image.png

以下に解凍して配置

C:\work\coherence\20.12\jdk-11.0.10_windows-x64_bin

ユーザー変数設定
image.png

Pathに以下の追加も忘れずに

%JAVA_HOME%\bin

動作確認

java -version

以下のような画面でればOK
image.png

Eclipseのダウンロード

以下からダウンロード
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_64

eclipse.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のソースした時にフォーマッターが適用されるように設定

image.png

xmlが間違ってもいないのにエラーが出てうざいので、チェックを全外し

image.png

スペルチェックもうざいので、チェックを外し

image.png

Apache Derbyのダウンロード

JDK7までは、JDKに同梱されてたけど、JDK8以降はないので、自分でダウンロードしようー
http://db.apache.org/derby/releases/release-10_14_2_0.cgi

image.png

解凍して以下に配置

C:\work\coherence\20.12\db-derby-10.14.2.0-bin

起動してみますかー以下実行するだけ

startNetworkServer.bat

こんな感じの画面でるよ

image.png

失敗した場合は以下を確認しましょー
※私はゾンビderby?がいて、ポート競合で失敗しましたー

derby.log

ElipseからApache Derbyに接続

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

表の作成

image.png

create table person (id integer primary key, name varchar(128));

image.png

リフレッシュしても、繁栄されなかったので、いったんDisconnectして、再connect
※connectする際に出力されるダイアログのパスワード保存にチェック入れると次回からダイアログが出なくなるので便利

image.png

image.png

PERSONテーブルできてること確認できましたー

image.png

Eclipse上にプロジェクトを作ろう

image.png

image.png

image.png

image.png

設定ファイルを作成&編集

coherence-20.12.jarを解凍しましょー
※7-zipというツールを使って私は解凍してます

image.png

中に以下の三つのファイルがあります

coherence-cache-config.xml
pof-config.xml
tangosol-coherence-override-dev.xml

上記三つのファイルをsrc配下にコピペしましょー

image.png

image.png

coherence-cache-config.xml

キャッシュの設定
私はテキストエディタでの編集が好きなので、テキストエディタをオープン

image.png

こんな感じに編集しよー

<?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>

ビルドパスの設定

image.png

まずは、作成したxml君たちを読み込む設定をしよー

image.png

以下を指定

C:\work\coherence\20.12\eclipse-jee-2020-12-R-win32-x86_64\eclipse\eclipse-workspace\CoherenceTest\bin

次に、coherenceのjarを参照する設定をしよー

image.png

以下を指定

image.png

こんな感じになってればOK
※順番大切なので、xmlの読み込み先にくるようにー

image.png

キャッシュのプログラミングしよー

Personクラス

xmlにpath記載してあるので、パッケージ名とクラス名の変更はしないように

image.png

image.png

ソースはこんな感じで

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記載してあるので、パッケージ名とクラス名の変更はしないように

image.png

ソースはこんな感じで

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台たちあげよー

ちゃんとスタートしたようですね

image.png

さあ、動作確認だー②

データ(キャッシュ&DBへ)を入れまーす
※derby立ち上げておいてね

ClientPutを実行

image.png

以下が表示されれば成功!

image.png

DBにデータが入ったか確認

image.png

image.png

DBにデータ入っているので、キャッシュ上にデータがあるか、それからデータが2台で分散してるか確認

jconsole(jdkに同梱されてるよ)で確認しよー
コマンドプロンプトで以下を実行

jconsole

あとで1号機を落とすので、2号機を選択
※情報はどっちでもみれるよー

image.png

1号機には、3つデータ入ってる

image.png

2号機には、1つデータ入ってる

image.png

データあるし、一応分散できてるね

次に1号機落としてみよー(コマンドプロンプト上でctrl+cとかで停止させてください)
2号機にデータうつるはず

image.png

お、成功しましたねー

アプリからもゲット(ClientGetを実行)してみよー

image.png

どうやらうまくいっているようですね

あとは各自色々なことして遊んでみてください

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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にアクセスすることでこの管理者ユーザーの作成ができます。
image.png

画面左に位置するAdministration Consoleに管理者の名前、パスワードを入力することで管理コンソール画面に移ります。
image.png

左側がメニューになり、レルムの設定、ユーザーなどを作成することができます。
初めに作られる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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.kt
import 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に移行すべきかどうかは、アプリの特性やプロジェクトの状況に応じて様々なので、それぞれで考えよう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】classファイルからクラスの情報を取得してみる

概要

Javaはビルドを行うとclassファイルを出力します。このclassファイルの中身をあえて解析しようという人は、あまりいないかもしれません。がしかし、ソースコードの分析とかに役立つかもということで、今回は試しにclassファイルの情報を取得してみたので、その方法を紹介してみたいと思います。

今回取得する情報

  • java.lang.Classで取得できるものを取得します。こちらの記事で紹介されているようにフィールド名やメソッド名など、クラスに関する基本的な情報は一通り取得できます。
  • javapで取得できるものを取得します。javapではクラスファイルはすべからくjavapしようの記事で紹介されている通り、ファイルサイズやチェックサム、コンパイルしたバージョンやコンスタントプールなどが参照できます。コンスタントプールについてはこちらの記事が詳しいです。コンスタントプールでは、呼出元から何のクラス・メソッドが、呼ばれているかの情報が取得できたりします。

処理の流れと参考記事

実装サンプル

【実行クラス】

Executer.java
public 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.java
public 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.java
public 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.java
public 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;
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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も見たところ、このような違いが書かれていました。

クラス仕様としての型定義をしたいならインタフェース
継承関係にあり、処理の再利用をしたいなら抽象クラス

引用元:【詳解】抽象クラスとインタフェースを使いこなそう!!

一旦、人間で例えましょう。
人間には日本人、アメリカ人など様々な人種がいます。
これらの人種はみんなしゃべることが出来ますが、言語が違います。
仮に親クラス「人間」を作るとしたら抽象メソッドで「言語」みたいなのを作れば、それぞれサブクラスで話し方の違いを表現できます。
これが抽象クラスの役割で、必ずあるような機能を再利用してますね。

では、特技について考えてみます。
必ずしも人間みんなが特技があるという訳ではないと思うので、抽象メソッドで必ず実装するような設計にはできないかと思います。
ですが、特技を実装したくなった時に特技のインターフェースを作っておけば、いつでも個別の人間ごとにプラスで処理が書けるイメージになります。これがインターフェースです。

さいごに

インターフェースどうだったでしょうか。
正直、私も完全に理解できてるか怪しいので指摘があれば教えてください!
途中で引用させていただいた記事は理解しやすいように思えたのでおススメします。

ご覧いただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む