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

Java 配列

学習ログ

配列

配列とは同一種類の複数データを並び順で格納するデータ構造。
配列の各要素には同一種類のデータしか格納できない。
配列の要素は0から始まる。
配列も変数と同様にデータ型を指定する。

配列変数の作成(宣言)
要素の型[] 配列変数名

//例
int[] score;         //scoreをint型の配列変数として宣言。
score = new int[5];  //配列変数scoreにnew演算子で生成したint型の要素を5つ代入している。

//配列変数の宣言と要素の作成と代入は同時に行うことも可能
int[] score = new int[5];  

//配列の中の要素を取り出す方法
score[0]; //配列変数scoreの中の1番目の要素を取得

//配列内の要素を変更する
score[0] = 50; //配列変数scoreの中の1番目の要素に30という値を代入することができる

//配列の要素数の取得
score.length; //配列変数にlengthというメソッドを使用すると配列内の要素の数を取得できる。
//lengthメソッドは文字列にも使用できる。String型変数名.length()という形になる。

//配列の省略記法
int[] score1 = new int[] {10, 20, 30, 40, 50};
int[] score2 = {10, 20, 30, 40, 50};

配列をforループで回す

//従来のfor文
for(int i = 0; i < 配列変数名.length; i++) {
  処理..
}
//従来のfor文 例
public class Main {
  public static void main(String[] args) {
    int[] score = {20, 30, 40, 50, 60};
    for(int i = 0; i < score.length; i++) {
      System.out.println(score[i]);
    }
  }
}

//拡張for文
for(要素の型 任意の変数名:配列変数名) {
  処理..
}
//拡張for文 例
public class Main {
  public static void main(String[] args) {
    int[] score = {20, 30, 40, 50, 60};
    for(int value : score) {
      System.out.println(value);
    }
  }
}

メモリ変数と配列

コンピュータは使用するデータをメモリに記録する。
メモリの中は碁盤の目のように区画整理されており、各区画には住所(アドレス)が振られている。
そして変数を宣言すると、空いている区画(どこが選ばれるかはわからない)を変数のために確保する(変数の型によって何区画を使用するか異なる)。
変数に値を代入するとは、確保していた区画に値を記録することである。

public class Main {
  public static void main{String[] args) {
    int[] a = {1, 2, 3};
    int[] b = b;
    b = a;
    b[0] = 100;
    System.out.println(a[0]); //100と出力される
  }
}

ガベージコレクション

javaの仕組みの一つ。
実行中のプログラムが生み出したメモリ上のゴミ(=どの変数からも参照されなくなったメモリ領域)を自動的に探し出して片付けてくれる。

NULL

「何もない」という意味。
nullが代入されると、参照型の変数はどこも参照していない状態になる。
・int[]型などの参照型変数に代入すると、その変数は何も参照しなくなる。
・int型などの基本型変数には代入することができない。

int[] score = {1,2,3}
score = null;
score[0];

2次元配列

2次元配列とは要素が縦横に並んでいるイメージ。

[0][0] [0][1] [0][2] [0][3]
[1][0] [1][1] [1][2] [1][3]
[2][0] [2][1] [2][2] [2][3]
//2次元配列の宣言
要素の型[][] 配列変数名 = new 要素の型[行数][列数]

//2次元配列の要素の取得
配列変数名[行の添字][列の添字]
//例
int[][] scores = new int[2][3]; //2行3列の配列
scores[0][0] = 40;
scores[0][1] = 50;
scores[0][2] = 60;
scores[1][0] = 70;
scores[1][1] = 80;
scores[1][2] = 90;
System.out.println(scores[1][1]); //80
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Drive API Set File Permission(GoogleDriveのファイルにパーミッションを設定しよう)

やあ!

AndroidアプリでファイルをGoogleDriveにアップロードして
そのファイルを他の人たちと共有したいときがあると思います。

今回はアップロードしたファイルに対して権限を付与する処理を書きますわよ!

権限は複数設定(setType)できるぞ!!
・ユーザ(user)ごと。(GoogleAccount名やemailを設定)
・グループ(group)ごと。(Google Groupのemail)
・ドメイン(domain)ごと。(com.jpとか)
・そのほか(anyone)。

※今回のソースコードは
 リンクを知っているすべてのユーザがそのファイルにアクセスできるようになりますわよ!

CreatePermission.java
    /**
     * 権限の設定
     * @param driveService drive
     * @param fileId fileID
     * @throws IOException create permission happen IOException
     */
    private void createPermission(Drive driveService,String fileId) throws IOException{
        JsonBatchCallback<Permission> callback = new JsonBatchCallback<Permission>() {
            @Override
            public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
                // Handle error
                System.err.println(e.getMessage());
            }
            @Override
            public void onSuccess(Permission permission, HttpHeaders responseHeaders) {
                // Handle success
                System.out.println("Permission ID: " + permission.getId());
            }
        };
        BatchRequest batch = driveService.batch();
        Permission userPermission = new Permission()
                .setType("anyone")
                .setRole("reader");
        driveService.permissions().create(fileId, userPermission)
                .setFields("id")
                .queue(batch, callback);
        batch.execute();
    }

!権限を削除するとき!
あらかじめ削除に必要なIDを取得する必要がある。

DeletePermissions.java
    File fileData; // 削除したいGoogleDriveのFile情報
    for(Permission permission : fileData.getPermissions()){
        driveService.permissions()
        // 権限を削除するファイルID、権限のID。
        .delete(fileData.getId() , permission.getId())
        .queue(batch,callback);
    }

!その他!
Googleフォルダ(親分)に権限を設定すると、
その親分に所属しているファイル(子分たち)はおなじ権限がつきます。
ですが、その権限を子分たちに対してDelete処理しても意味ないから気を付けてね!!
親分倒すか、別の親分を用意して!

あとは雰囲気で何とか調整してください。
何かあったらコメントください。より詳細は以下URLにあるよ。

<サンプル元>
https://developers.google.com/drive/api/v3/manage-sharing

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

Javaで読み込みできる形式のecdsaのkey pairの作り方

openssl ecparam -genkey -name secp256k1 -out key-pair.pem
openssl pkcs8 -topk8 -inform pem -in key-pair.pem -outform pem -nocrypt -out private.pem
openssl ec -in key-pair.pem -pubout -outform pem -out public.pem

private keyは 

-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----と改行コード をtrim

KeyFactory factory = KeyFactory.getInstance("EC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
PrivateKey privateKey = factory.generatePrivate(keySpec);

public keyも同様にtrimして

X509EncodedKeySpec keySpec2 = new X509EncodedKeySpec(Base64.getDecoder().decode(key2));
PublicKey publicKey = factory.generatePublic(keySpec2);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring gradleでサーバー実行可能JAR作成とWAR作成の仕方

Spring boot 実行可能Jar

環境:macOS, Spring Tools
Package Explorerのプロジェクト名を右クリック。

Run as ⇨ Run Configuration
Gradele Projectを右クリック。
Gradle Tasksタブをクリック。
Gradle Tasks: 欄に build と記述。

Working Directory欄に プロジェクトのルートフォルダを指定。
右下の Run をクリック。

すると

プロジェクトのルート/build/libs

に プロジェクト名.jar が出来ている。

(以下試行中)サーバにアップロード 
上記の任意のフォルダにアップロード。

terminal
java -jar test.jar  

で実行するとjarに内臓されたtomcatが立ち上がる。
サーバーを止めるときはcontrol + c
でも私の場合、これで実行してもアプリが立ち上がって一見動いているだけで、ページにアクセスしても反応がなかった。

Spring Boot 実行可能War

プロジェクトを新規で作るときのメニューかbuild.gradleでjarが指定されているところをwarに変える。
build.gradleに

build.gradle
apply plugin:'war'
war {
        enabled = true
        archiveName 'sample.war'
    }

を追加
gradleをリフレッシュして、jarの時と同じ手順でconfigurationを開き

Gradle Tasks:欄に war と記述。
すると同じように

プロジェクトルート/build/libs に warファイルが出来ている。

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

Spring サーバー実行可能JAR

Spring boot 実行可能Jar

環境:macOS, Spring Tools
Package Explorerのプロジェクト名を右クリック。

Run as ⇨ Run Configuration
Gradele Projectを右クリック。
Gradle Tasksタブをクリック。
Gradle Tasks: 欄に build と記述。

Working Directory欄に プロジェクトのルートフォルダを指定。
右下の Run をクリック。

すると

プロジェクトのルート/build/libs

に プロジェクト名.jar が出来ている。

(以下試行中)サーバにアップロード 

上記の任意のフォルダにアップロード。

terminal
java -jar test.jar  

で実行するとjarに内臓されたtomcatが立ち上がる。
サーバーを止めるときはcontrol + c

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

Spring gradleでサーバー実行可能JAR作成の仕方

Spring boot 実行可能Jar

環境:macOS, Spring Tools
Package Explorerのプロジェクト名を右クリック。

Run as ⇨ Run Configuration
Gradele Projectを右クリック。
Gradle Tasksタブをクリック。
Gradle Tasks: 欄に build と記述。

Working Directory欄に プロジェクトのルートフォルダを指定。
右下の Run をクリック。

すると

プロジェクトのルート/build/libs

に プロジェクト名.jar が出来ている。

(以下試行中)サーバにアップロード 
上記の任意のフォルダにアップロード。

terminal
java -jar test.jar  

で実行するとjarに内臓されたtomcatが立ち上がる。
サーバーを止めるときはcontrol + c
でも私の場合、これで実行してもアプリが立ち上がって一見動いているだけで、ページにアクセスしても反応がなかった。

Spring Boot 実行可能War

プロジェクトを新規で作るときのメニューかbuild.gradleでjarが指定されているところをwarに変える。
build.gradleに

build.gradle
apply plugin:'war'
war {
        enabled = true
        archiveName 'sample.war'
    }

を追加
gradleをリフレッシュして、jarの時と同じ手順でconfigurationを開き

Gradle Tasks:欄に war と記述。
すると同じように

プロジェクトルート/build/libs に warファイルが出来ている。

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

ByteBufferの複製には、バイトオーダーの再設定が必要

バイト列を扱う際、バイトオーダーを意識しないといけない場合があります。

このような場合、Javaでは、ByteBufferクラスを使用すると便利です。ByteBufferにはバイトオーダーを設定でき、複数バイトから構成される値を簡単に扱えるためです。例えば、次のコードでは、リトルエンディアンの値を扱っています。

LittleEndianSample.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class LittleEndianSample {
    public static final void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES);

        // リトルエンディアンの16ビット整数。下位バイトが0x11、上位バイトが0x22。
        buffer.order(ByteOrder.LITTLE_ENDIAN)
            .put((byte) 0x11)
            .put((byte) 0x22)
            .rewind();

        System.out.printf("0x%x\n", buffer.getShort());
    }
}

実行結果は次の通りです。

0x2211

ところが、上記のByteBufferを複製すると、バイトオーダーが想定外のものになってしまいます。例えば、リトルエンディアンのByteBufferを返すメソッドを書いているとします。その呼び出し元がByteBufferの内容を変更できないように、読み取り専用で返すことにします。しかし、次のようなコードでは、呼び出し元がビッグエンディアンのByteBufferを受け取ってしまいます。

BadSample.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class BadSample {
    private static class BufferObject {
        private final ByteBuffer buffer;

        public BufferObject() {
            buffer = ByteBuffer.allocate(Short.BYTES);

            // リトルエンディアンの16ビット整数。下位バイトが0x11、上位バイトが0x22。
            buffer.order(ByteOrder.LITTLE_ENDIAN)
                .put((byte) 0x11)
                .put((byte) 0x22)
                .rewind();
        }

        private ByteBuffer getBuffer() {
            // 読み取り専用で複製(誤り。ビッグエンディアンになる)
            return buffer.asReadOnlyBuffer();
        }
    }

    public static final void main(String[] args) {
        BufferObject bufferObject = new BufferObject();

        ByteBuffer buffer = bufferObject.getBuffer();

        System.out.printf("0x%x\n", buffer.getShort());
    }
}

実行結果は次のようになります。上位バイトと下位バイトとが入れ替わり、ビッグエンディアンになっていることがわかります。

0x1122

なぜこのような挙動となるのでしょうか。

OpenJDKのIssue Trackerに投稿されたIssue JDK-7178109によると、この挙動は仕様とのことです。

新規に作成されたByteBufferのバイトオーダーは、常にビッグエンディアンとなります。つまり、allocate()やallocateDirect()以外にも、下記のメソッドから返されるByteBufferのバイトオーダーは、元のByteBufferのバイトオーダーにかかわらず、常にビッグエンディアンです。

  • duplicate()
  • asReadOnlyBuffer()
  • wrap()
  • slice()
  • alignedSlice()

Java SEでは、バージョン9のAPIドキュメントから、上記の各メソッドに、作成されたByteBufferのバイトオーダーがビッグエンディアンになることが記されています。しかし、AndroidのAPIドキュメントでは、現時点(2019/7/9)ではそのような記述はありません。

以上から、バイトオーダーを保ったままByteBufferを複製するには、複製後にorder()でバイトオーダーを再度設定する必要があります。

DuplicationSample.java
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class DuplicationSample {
    private static class BufferObject {
        private final ByteBuffer buffer;

        public BufferObject() {
            buffer = ByteBuffer.allocate(Short.BYTES);

            // リトルエンディアンの16ビット整数。下位バイトが0x11、上位バイトが0x22。
            buffer.order(ByteOrder.LITTLE_ENDIAN)
                .put((byte) 0x11)
                .put((byte) 0x22)
                .rewind();
        }

        private ByteBuffer getBuffer() {
            // 読み取り専用で複製。バイトオーダーを設定する必要がある。
            return buffer.asReadOnlyBuffer().order(buffer.order());
        }
    }

    public static final void main(String[] args) {
        BufferObject bufferObject = new BufferObject();

        ByteBuffer buffer = bufferObject.getBuffer();

        System.out.printf("0x%x\n", buffer.getShort());
    }
}

実行結果は次の通りです。バイトオーダーが正しくリトルエンディアンになりました。

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

Resilience4j の TimeLimiter でメソッド呼び出しにタイムアウトをつける

背景

Azure CosmosDBであったり、AWS S3であったり、FAXを送信するサービスであったり、外部のサービスを呼び出すSDKを使うシーンが多くなってきた。

これまで普通に生きてきた世界は、自分で管理するデータベースサーバーの生死だけを気にすればよくて、ネットワーク的な距離もごく近傍。すべてが想定の範囲内の出来事だったが、こうした外部のサービスを利用するときには単純にはいかない。

ネットワークの遅延は安定せず、時には秒単位での遅延も起こりうる。多くのサービスはホスト名でエンドポイントが提供され、IPアドレスは予告なく変更になる。

こうした不安定なイベントが発生したとき、各サービスをHTTPなりで呼び出してブロッキングしているスレッドが急激にたまってしまい終いにはサービス全体の停止を引き起こすようなことがある。

つまりブロッキングするような呼び出しは、関数呼び出しにタイムアウトをつけてメインのHTTPスレッドを埋めないような注意が必要だ。

Rubyならばわりと簡単に書ける。

require 'timeout'

def callAnotherWorld
    sleep 3
    return "何か外部のサービスを呼び出した結果"
end

begin
  result = Timeout.timeout(1) do # 1秒でタイムアウト
    callAnotherWorld()
  end
rescue Timeout::Error
  result = ""
end

p result                         # タイムアウトするのでブランクになる

Javaでもだいたい同じようなコードになる。

private final ExecutorService threadPool = Executors.newCachedThreadPool();

private String callAnotherWorld() {
    Future<String> f = threadPool.submit(() -> innerCallAnotherWorld());

    while (true) {
        try {
            f.get(1, TimeUnit.SECONDS);  // 1秒でタイムアウト
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException retry) {
        }
    }
}

private String innerCallAnotherWorld() {
    return "何か外部のサービスを呼び出した結果";
}

RubyもJavaもバックグラウンドのスレッドでメソッドが実行され、呼び出し側スレッドはその完了を待つという動作である。

Javaの場合、スレッドプールの存在と、Futureの存在が見えてしまっており、少し残念な気持ちになる。

Resilience4J

そこで Resilience4J である。

Resilience4J は最近のマイブームで、分散アーキテクチャにおけるシステムの安定性を向上させるべく、以下のパターンが簡単に利用できるようになる。

  • CircuitBreaker
  • RateLimitter
  • BulkHead
  • Retry
  • TimeLimiter

メソッド呼び出しに対するタイムアウトを実現するには、TimeLimiterを利用すればよい。
CircuitBreakerの使い方などはすぐにヒットするが、TimeLimiterについてはあまり情報がないので、自分用の備忘として書いておく。

private final ExecutorService threadPool = Executors.newCachedThreadPool();

private final TimeLimiterConfig config = TimeLimiterConfig.custom()
        .timeoutDuration(Duration.ofMillis(1000))
        .cancelRunningFuture(true)
        .build();

private String callAnotherWorld() {

    Callable<String> callable = TimeLimiter
        .decorateFutureSupplier(
            TimeLimiter.of(config),
            () -> threadPool.submit(() -> innerCallAnotherWorld())
        );

    return Try.of(callable::call)
            .getOrElse("");
}

あんまり短くならないが、catchとか例外処理がなくなり、ビジネスロジックにフォーカスできるコードになる。

Resilience4Jが提供する機能は、いまどきの分散環境において、必須となるデザインパターンで、非常に勉強になる。

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

究極のハマり 〜tomcat 再起動編〜

今日はTomcatを使いローカル環境でユーザー認証のテストを行なっていた時の典型的なハマり。

$./shutdown.sh <Tomcatを一旦終了>
        
        ↓

$./startup.sh  <再びTomcat再起動>

めんどくさい超基本的な作業だが難しいシステムを作る程基本を忘れている事がある。

何をしても行かない時はしっかりTomcatを再起動してから実行してみる。

あ、こんなんでうまくいった、、、、ってタイミングが必ず一度はあるはず。

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

GAE/JのDatastore関連備忘録

Datastoreで使用できるタイプ

以下に一覧がある。新しいjava.timeパッケージ関連は使えないのでDate型でなんとかする必要あり。
https://cloud.google.com/appengine/docs/standard/java/datastore/entities#Java_Properties_and_value_types

ローカルサーバーでDatastoreを参照する方法

使用言語によって細かな手順は違うらしいけど、Javaの場合は以下
1. ローカルサーバーを立ち上げる
2. 開発コンソール http://localhost:8080/_ah/admin にアクセスする
https://cloud.google.com/appengine/docs/standard/java/tools/using-local-server#browsing-datastore

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