20190413のJavaに関する記事は7件です。

プログラミング学習記録56〜動画とエディタを行き来するのが疲れるからデュアルディスプレイにしたい〜

今日やったこと

  • Javaに触れる
  • 動画教材とエディタを行き来するのが精神的に疲れるので、デュアルディスプレイの導入を検討する

今日は講義でJavaに触れました。

実行環境は用意されているものを使いました。
Cloud9のような感じです。

Javaに触れた感想としては、今の所はまだ入門段階なので、他の言語と大差ないなという印象ですね。
ただ、Rubyと比べると記述量が多い気がします。

あと、以前から思っていたことなのですが、動画を見ながらエディタにコードを打ち込んでいく作業をするときは2画面でやったほうがいいですね。

1画面でやろうとすると、画面を半分半分にして小さい画面で頑張るか、別のウィンドウで開いて切り替えながらやることになります。

これが意外と面倒になってきたので、デュアルディスプレイ(2画面)で作業していきたいと思います。
最初はお金がかかっちゃいますけど、その後の作業効率考えたら導入した方がいいです。

調べてみたらMacBookの場合はiPadもサブディスプレイに使えるそうなので、まずはiPadで試していきます。

iPadで画面の大きさが足りないようであれば、別にディスプレイを用意したいと思います。

ということで、明日からもプログラミング学習頑張ります。

おわり

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

Mockitoの記述方法

JUnit + Mockitoでテストする際のMockitoの記述方法が紹介するページによりマチマチなので自分なりに整理した。

環境

  • Mockito v2.23.9
  • JUnit v4.12(JUnit5にしたい)

Mockitoの機能

Mock

対象インスタンスのメソッドをすべてモック化して置き換えたい場合に使用する。
デフォルト状態では、オブジェクトの場合、戻り値があるメソッドはnullを返すようになる。(Collectionでは空のCollectionだったり、プリミティブな値では0とかfalseとか...)

@Mockのアノテーションを付与することでモック化できる。
また、テスト対象となるクラスのインスタンスに@InjectMocksを付与することで、
インスタンス内の@Injectされたメンバーのインスタンスに@Mockのモックインスタンスを差し込むことができる。

Spy

対象のインスタンスの特定のメソッドだけを置き換えたい場合に使用する。
私はあまり使いこなせていない。
getterやらのメソッドはそのまま使いつつ1つのメソッドだけ置き換えたい場合とかがある?

@Spyのアノテーションを付与することで利用できる。
インスタンスは自分で生成する必要がある。
@InjectMocksで注入することはできない。

Captor

主に引数の値をキャプチャして検証するのに使用する。
引数がオブジェクトの場合、eqのような標準のマッチャでは検証できない。
このとき、Captorが有効である。

引数をキャプチャする場合、ArgumentCaptorクラスのインスタンスを定義し@Captorを付与する。

基本のテストの書き方

DIを利用した設計をしていることを前提に、Mockitoを使った基本的な記述をまとめる。

基本方針

  • MockitoのモードはStrictStubsにする。
    • MockitoにはSilent, Strict(v2のデフォルト), StrictStubsの3つのモードがある。
    • StrictStubsにすると、スタブとなるモックの引数ミスマッチも検出してくれるので、一番厳しく設定。
  • 初期化は原則アノテーションで行う。
    • mock(XX.class)spy(new XX()), ArgumentCaptor.forClass(XX.class)を使った初期化方法もあるが、アノテーション使ったほうがシンプルに書ける。
    • @InjectMocksを付与する対象はインターフェースにしないよう注意。(newできない)
  • verifyは書かなくてよい。
    • v2からは用意したモックスタブが呼ばれなかった場合、非チェック例外が発生するので、呼ばれたかいちいちverifyで検証する必要はないと思う。
    • あるとすれば、複数回呼ばれた回数をカウントしたい場合くらい?
  • モックの記述はdoXX().when()の形式で書く。
    • ここは賛否両論あり、公式も態度明らかにしていない感じである。 you may prefer to use these methods in place of the alternative with when(), for all of your stubbing calls. とのこと。
    • 個人的には、戻り値の有無やMockとSpyの違いで記述方法をいちいち覚えるのは面倒。見た目も煩雑になると思う。
    • よって、すべての記述で使えるdoXX().when()の形式に統一する。
    • ただし、後述する呼び出しによって戻り値を変えるケースではwhen().thenReturn()形式での記述が必要になる。

前準備

  1. テストでMockitoを使うためにおまじない@RunWith(MockitoJUnitRunner.StrictStubs.class)テストクラスに付与する。
    • @Ruleで同様の設定をしても良い。(Strictness.STRICT_STUBS)
  2. テスト対象となるクラスの変数に@InjectMocksを付与する。
  3. モック化したいクラスの変数に@Mockを付与する。
    • @InjectMocksおよび@Mockのアノテーションを付与するだけでインスタンスが生成される。
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
...

@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class XXTest{
  @InjectMocks
  XXInteractor target;

  @Mock
  IXXRepository repoMock;

  ...
}

テストケース

  1. @Testを付与したvoid publicメソッドを定義する。
  2. モックインスタンスのモック化したいメソッドの定義を記述する。
  3. テスト対象のメソッドを呼ぶ。
  4. 結果を検証する。

テスト対象が以下のとき、

public class XXInteractor{
  @Inject
  IXXRepository repository;

  XXEntity fetch(String id) throws NotFoundException{
    try{
      return repository.findBy(id);
    }
    catch(UseCaseNotFoundException e){
      throw new NotFoundException();
    }
  }
}

以下のようなテストが書ける。

  private static final String ID = "ID";

  @Test
  public void testFetchSuccess() throws Exception{
    // 引数にIDが与えられたときだけ正常なXXEntityを返す。(その他の場合、null)
    doReturn(new XXEntity(ID))
      .when(repoMock).findBy(eq(ID));

    XXEntity result = target.fetch(ID);

    assertThat(result.getId(), is(ID));
  }

  @Test(expected = NotFoundException.class)
  public void testFetchFailureWithNotFound() throws Exception{
    doThrow(new RepositoryNotFoundException())
      .when(repoMock).findBy(eq(ID));

    target.fetch(ID);
  }

モック化ユースケース

基本編

基本的なモック化パターンを以下に示す。

モック化したメソッドで値を返す

// 戻り値がある場合
doReturn(new XXEntity(ID))
  .when(targetMock).fetch(eq(ID));

// 戻り値がない場合
doNothing().when(targetMock).delete(eq(ID))

モック化したメソッドで例外を発生させる

doThrow(new XXException()).when(targetMock).fetch(eq(ID))

doThrow(new XXException()).when(targetMock).delete(eq(ID))

Spyしたインスタンスのメソッドを置き換える

// 戻り値を返す
doReturn(new XXEntity()).when(targetSpy).fetch()

// 例外を発生させる
doThrow(new XXException()).when(targetSpy).fetch()

応用編

どんな引数でも呼ばれたらレスポンスしたい

doReturn(new XXEntity(ID))
  .when(targetMock).fetch(any())

同じメソッドで呼び出しごとに違う値を返したい

// 引数がないケース、あるいは、同じ引数で呼び出しごとに別の値を返したいケース
// このケースはdoReturnでは書けないのでthenReturn形式で記述する。
when(targetMock.getNextKey())
  .thenReturn("ID-1")   // 一度目の呼び出し
  .thenReturn("ID-2");  // 二度目の呼び出し

// 引数ごとに違う値を返すケース(普通にそれぞれ定義)
doReturn(new XXEntity("ID-1")).when(targetMock).fetch(eq("ID-1");
doReturn(new XXEntity("ID-2")).when(targetMock).fetch(eq("ID-2");

プリミティブでない引数を検証したい

引数がプリミティブな場合はeqのみで検証できるが、引数がオブジェクトやリスト・マップ系の場合は検証できないため、Captorと組み合わせる必要がある。

テスト対象が以下のとき、

public class XXInteractor{
  @Inject
  IXXRepository repository;

  void update(XXEntity entity){
    repository.update(entity);
  }
}

以下のように検証する。

  private static final String ID = "ID";

  @InjectMocks
  XXUseCase target;

  @Mock
  IXXRepository repoMock;

  // Captorを定義
  @Captor
  ArgumentCaptor<XXEntity> argCaptor;

  @Test
  public void testUpdateSuccess(){
    // 引数をキャプチャするためにモックを定義
    doNothing().when(repoMock).update(argCaptor.capture());

    target.update(ID);

    // キャプチャした引数を検証
    assertThat(argCaptor.getValue().getId(), is(ID));
  }

モックに渡ってきた引数を書き換えたい

引数の一部を戻り値に使うC#でいうrefのような実装をしているメソッドをモックにするケース。
このメソッド定義はTestableでないので、根本的には設計変更で解決すべきだが、
どうしても引数でもらったインスタンスの中身を書き換えたい場合は以下の方法で実現できる。

doAnswer( invocation -> {
  Object[] args = invocation.getArguments();

  // 引数をそのクラスにキャストすることで、そのインスタンスを操作できるようになる。
  // このケースでは第二引数を書き換えるのでargs[1]
  ((XXOutputParam) args[1]).setId("ID");
  return null; //戻り値がvoidならnull
}).when(useCaseMock).get(any(), any())

モックの動きをリセットしたい

@Beforeのメソッドあたりで毎回呼ぶと良い。

reset(mock);

参考

https://static.javadoc.io/org.mockito/mockito-core/2.23.4/org/mockito/Mockito.html

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

【Android】Google MapのinfoWindowにウェブ上の画像を表示する

1.プロジェクト作成

新規プロジェクトを作成します。
Google Maps Activityを選択し、プロジェクト名を入力して進みます。
自動的に生成されるres/values/google_maps_api.xmlのYOUR_KEY_HEREの部分を、取得してある自身のAPIキーで置き換えておきます。

google_maps_api.xml
<resources>
<!-- 略 -->
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string>
</resources>

この時点で一度プログラムを起動し、地図がちゃんと表示される事を確認します。

2.レイアウトファイルを記述する

infoWindow用のレイアウトを作成します。今回はLinearLayoutにImageViewが一つあるだけのシンプルなものにします。

info_window_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

3.InfoWindowAdapterの記述

MapsActivity.javaのonMapReadyメソッドにて、InfoWindowAdapterを実装します。

MapsActivity.java
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
//     〜略〜
        mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
            Marker lastMarker;
            View infoWindow;

            @Override
            public View getInfoWindow(Marker marker) {
                return null;
            }

            @Override
            public View getInfoContents(Marker marker) {
//              今回はこちらに処理を記述
                return null;
            }
        });

infoWindowAdapterはgetInfoWindow()メソッドとgetInfoContents()メソッドの両方をオーバーライドする必要があり、かつどちらかに処理を記述しもう片方はnullを返す必要があります。今回はgetInfoContents()に処理を記述します。また、処理の都合上必要な変数lastMarkerとinfoWindowをinfoWindowAdapterのクラス内で宣言しておきます。

4.getInfoContents()メソッドの記述

            @Override
            public View getInfoContents(Marker marker) {
                if(lastMarker==null || !lastMarker.equals(marker)){
                    lastMarker = marker;
                    infoWindow = getLayoutInflater().inflate(R.layout.info_window_layout,null);
                    ImageView img = infoWindow.findViewById(R.id.img);
                    new DownloadImageTask(img,marker).execute(imgUrl);
//                      ↑このあと実装
                }
                return infoWindow;
            }

最後まで実装してから細かく処理を追えばわかることですが、マーカーをクリックしてinfoWindowを表示させようとするとgetInfoWindow()メソッドは合計2回呼ばれることになります。1回目で画像をダウンロードする処理が走り、2回目はreturn infoWindow;だけ実行され、無限ループにならないようにしています。

5.画像のダウンロード処理のクラスを記述

private class DownloadImageTask extends AsyncTask<String,Void, Bitmap>{
        ImageView img = null;
        Marker marker = null;

        DownloadImageTask(ImageView img, Marker marker){
            this.img = img;
            this.marker = marker;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap bmp = null;
            try{
                String urlStr = strings[0];
                URL url = new URL(urlStr);
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setRequestMethod("GET");
                con.connect();
                int resp = con.getResponseCode();
                switch (resp){
                    case HttpURLConnection.HTTP_OK:
                        InputStream is = con.getInputStream();
                        bmp = BitmapFactory.decodeStream(is);
                        is.close();
                        break;
                    default:
                        break;
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bmp;
        }

        @Override
        protected void onPostExecute(Bitmap bmp){
            img.setImageBitmap(bmp);
            marker.showInfoWindow();
        }
    }

画像のダウンロード処理のクラスであるDownladImageTaskを記述します。画像のダウンロードは非同期で行う必要があるのでAsyncTaskを継承しています。画像のURLは、execute()で呼び出すときにStringで受け渡し、doInbackground()のstrings[0]で受け取ります。画像のダウンロードが完了したあとで呼ばれるonPostExecute()でImageViewに画像をセットしており、そのあとmarker.showInfoWindow()を呼ぶことでinfoWindowに画像が表示されます。

6.MapsActivity.javaの全体

マーカーはデフォルトの位置であるシドニー、画像は、ウェブ版のGoogle mapでシドニーで検索したときに出たもののurlを指定しています。

MapsActivity.java
package com.example.test;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        final String imgUrl = "https://lh5.googleusercontent.com/p/AF1QipNUrZvzjGohRsYfuwIpCS2MjYdAq_3xruYM5imS=w408-h271-k-no";

        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
        mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
            Marker lastMarker;
            View infoWindow;

            @Override
            public View getInfoWindow(Marker marker) {
                return null;
            }

            @Override
            public View getInfoContents(Marker marker) {
                if(lastMarker==null || !lastMarker.equals(marker)){
                    lastMarker = marker;
                    infoWindow = getLayoutInflater().inflate(R.layout.info_window_layout,null);
                    ImageView img = infoWindow.findViewById(R.id.img);
                    new DownloadImageTask(img,marker).execute(imgUrl);
                }
                return infoWindow;
            }
        });
    }

    private class DownloadImageTask extends AsyncTask<String,Void, Bitmap>{
        ImageView img = null;
        Marker marker = null;

        DownloadImageTask(ImageView img, Marker marker){
            this.img = img;
            this.marker = marker;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap bmp = null;
            try{
                String urlStr = strings[0];
                URL url = new URL(urlStr);
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setRequestMethod("GET");
                con.connect();
                int resp = con.getResponseCode();
                switch (resp){
                    case HttpURLConnection.HTTP_OK:
                        InputStream is = con.getInputStream();
                        bmp = BitmapFactory.decodeStream(is);
                        is.close();
                        break;
                    default:
                        break;
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bmp;
        }

        @Override
        protected void onPostExecute(Bitmap bmp){
            img.setImageBitmap(bmp);
            marker.showInfoWindow();
        }
    }
}

参考

https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter
https://stackoverflow.com/questions/36335004/how-to-get-image-in-infowindow-on-google-maps-with-picasso-android

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

ライブラリ共有、JavaからMySqlへアクセス

DBを利用するだけなのに、DAO、entity、XMLを3セット書かなくちゃ!
フレームワークは面倒くさいと思いつつ、今回のライブラリを作成した。

1.メリット

・XML不要(sql文を記述する設定ファイル)
・DAO不要(sqlを実行するクラス)
・フレーム不要

・・・・・・・

Q:なにも不要?ちゃんとしている機能はないじゃない??
A:いえいえ、sql及びentityによるDB操作はしっかりサポートしているし、かつ一つクラスで完結!

2.ライブラリ紹介

sqlの作成を手助ける

早速サンプルコードを見ましょう!

    SqlWritter writter = new SqlWritter();
    writter.select("*")
            .from("speech_data")
            .where("name")
            .like("新垣結衣 ニンゲン観察バラエティモニタリング");

    System.out.println(writter);

?コンソール出力

select
*
from
speech_data
where
name LIKE '%新垣結衣 ニンゲン観察バラエティモニタリング%'

Q: ('ω') うわあああ!sql文を生成してくれたねといいたかったところ、どういうメリットがあるか?
A:確かにこれだけだと、メリットはあんまり実感できなさそうだね。しかし、以上の機能をコアとして、さらに便利な機能を提供できるんだって!

sqlによる検索

生成したsqlを用いて、DAO不要のメリットを見てみよう。

    List<SpeechData> entities;
    try (Accessor accessor = new Accessor()) {
        entities= accessor.selectBySql(writter, SpeechData.class);
    }

上記のコードを簡単に説明してみると、上記はsql文による検索操作だ。
 SpeechDataは事前作成したspeech_dataというテーブルのエンティティクラス
 entitiesは結果を格納するためのリスト
 Accessorはデータベースへアクセスするためのクラス
 writterは先ほど生成したSQL

Q:うんーーー、よくわからない!
A:大丈夫、覚えておいてほしいのはAccessorクラスはすべてのエンティティクラスに対応できるため、DAOはもう不要だ!

下記のコードを追加して、実行結果を見てみよう。

    for (SpeechData entity : entities) {
        System.out.println(String.format("ファイル:%sの分析結果:", entity.getName()));
        System.out.print(String.format("喜び:%s,", entity.getJoy()));
        System.out.print(String.format("悲しみ:%s,", entity.getSorrow()));
        System.out.print(String.format("怒り:%s,", entity.getAnger()));
        System.out.print(String.format("エネルギー:%s,", entity.getEnergy()));
        System.out.println(String.format("穏やかさ:%s", entity.getCalm()));
        System.out.println();
    }

?コンソール出力

ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_00.wavの分析結果:
喜び:0,悲しみ:9,怒り:0,エネルギー:0,穏やかさ:40

ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_01.wavの分析結果:
喜び:18,悲しみ:2,怒り:0,エネルギー:16,穏やかさ:29

ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_02.wavの分析結果:
喜び:11,悲しみ:0,怒り:0,エネルギー:11,穏やかさ:38



Q:怪しいデータが出てきた!
A:ごめんごめん、音声による感情識別データだ。データの中身はとりあえず無視して、実行したsqlは無事に結果を戻しれくれた。

エンティティによる検索

フレームのように、エンティティによる検索も可能!

    SpeechData speechData = new SpeechData();
    speechData.setName("新垣結衣 ニンゲン観察バラエティモニタリング_02.wav");

    List<SpeechData> entities;
    try (Accessor accessor = new Accessor()) {
        entities = accessor.selectByEntity(speechData);
    }

    for (SpeechData entity : entities) {
        System.out.println(String.format("ファイル:%sの分析結果:", entity.getName()));
        System.out.print(String.format("喜び:%s,", entity.getJoy()));
        System.out.print(String.format("悲しみ:%s,", entity.getSorrow()));
        System.out.print(String.format("怒り:%s,", entity.getAnger()));
        System.out.print(String.format("エネルギー:%s,", entity.getEnergy()));
        System.out.println(String.format("穏やかさ:%s", entity.getCalm()));
        System.out.println();
    }

?コンソール出力

ファイル:新垣結衣 ニンゲン観察バラエティモニタリング_02.wavの分析結果:
喜び:11,悲しみ:0,怒り:0,エネルギー:11,穏やかさ:38

Q:エンティティによる検索だね
A:はい、ライブラリだけど、機能は半端ない!

3.ライブラリ共有

ライブラリを使ってみたい方は、下記のリンクからソースをダウンロードすることができる。
https://github.com/chaofanzheng/leadinge

わからない時があれば、下記の仕様を参考すれば助けになる!!

4.ライブラリを使用するための仕様

システムプロパティ

system.properties
    #mysqlのユーザID(各自の設定に従う)
    USER_ID = root

    #mysqlのパスワード(各自の設定に従う)
    USER_PASSWORD = MySql

    #mysqlへアクセスのURL(各自の設定に従う)
    DB_URL = jdbc:mysql://localhost:3306/speech_recognition?useSSL=false&&allowPublicKeyRetrieval=true

エンティティ

SpeechData.java
//CommonEntityを継承する必要がある
public class SpeechData extends CommonEntity {

    //コンストラクタ
    public  SpeechData() {
        //データタイプの初期化(テーブル情報に合わせて設定する)
        columnsType = new HashMap<String,Class<?>>();
        columnsType.put("id", int.class);
        columnsType.put("name", String.class);
        columnsType.put("wav", byte[].class);
        columnsType.put("error", int.class);
        columnsType.put("calm", int.class);
        columnsType.put("anger", int.class);
        columnsType.put("joy", int.class);
        columnsType.put("sorrow", int.class);
        columnsType.put("energy", int.class);
        //テーブル名を設定する(スネークケース)
        setTableName("speech_data");
    }

    public int getId() {
        return (int)columns.get("id");
    }

    public void setId(int value) {
         columns.put("id",value);
    }

    public String getName() {
        return (String)columns.get("name");
    }

    public void setName(String value) {
         columns.put("name",value);
    }

    public byte[] getWav() {
        return (byte[])columns.get("wav");
    }

    public void setWav(byte[] value) {
         columns.put("wav",value);
    }

    public int getError() {
        return (int)columns.get("error");
    }

    public void setError(int value) {
         columns.put("error",value);
    }

    public int getCalm() {
        return (int)columns.get("calm");
    }

    public void setCalm(int value) {
         columns.put("calm",value);
    }
    public int getAnger() {
        return (int)columns.get("anger");
    }

    public void setAnger(int value) {
         columns.put("anger",value);
    }

    public int getJoy() {
        return (int)columns.get("joy");
    }

    public void setJoy(int value) {
         columns.put("joy",value);
    }

    public int getSorrow() {
        return (int)columns.get("sorrow");
    }

    public void setSorrow(int value) {
         columns.put("sorrow",value);
    }

    public int getEnergy() {
        return (int)columns.get("energy");
    }

    public void setEnergy(int value) {
         columns.put("energy",value);
    }
}

DBを操作するためには

Select.java
    //sqlを作成した場合は...もちろん、外部ファイルでも手書きでもオケ
    SqlWritter writter = new SqlWritter();
    writter.select("*")
            .from("speech_data")
            .where("name")
            .like("新垣結衣 ニンゲン観察バラエティモニタリング");

    List<SpeechData> entities;

    //DBアクセサの初期化
    try (Accessor accessor = new Accessor()) {
        //sqlによる選択
        entities = accessor.selectBySql(writter, SpeechData.class);

        //エンティティの初期化
        SpeechData speechData = new SpeechData();
        speechData.setName("新垣結衣 ニンゲン観察バラエティモニタリング_02.wav");
        //エンティティによる選択
        entities = accessor.selectByEntity(speechData);
    }
Insert.java
    //sqlを作成した場合は...もちろん、外部ファイルでも手書きでもオケ
    SqlWritter writter = new SqlWritter();  
    writter.insertInto("speech_data")
            .colums("id","name")
            .values(999,"新垣結衣 ニンゲン観察バラエティモニタリング_999.wav");

    //DBアクセサの初期化
    try (Accessor accessor = new Accessor()) {
        //sqlによる選択操作
        accessor.insertBySql(writter);

        //エンティティの初期化
        SpeechData speechData = new SpeechData();
        speechData.setId(999);
        speechData.setName("新垣結衣 ニンゲン観察バラエティモニタリング_999.wav");
        //エンティティによるインサート操作
        accessor.insertByEntity(speechData);
    }

5.ソースコードについて

現状ではinsertとselectしかサポートしていないので、
更新や削除でもできるようにしたい場合は...各自がコードを改修してください。

実際にDBへアクセスするときに使っていただくか、またはソースが参考になれば、
うれしいと思います。

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

eclipse上でStruts2のデモプロジェクトを作ります

プロジェクトの作成

まず、eclipseを開いてプロジェクトを作成しましょう。
ファイル -> 新規 -> その他と進んで「動的Webプロジェクト」を選択して「次へ」を押しましょう。
スクリーンショット 2019-04-10 7.18.11.png
「プロジェクト名」にプロジェクト名(struts2_demo)を入力し、適当なTomcatサーバを選択して完了を押しましょう。
スクリーンショット 2019-04-10 7.20.49.png

プロジェクトにStruts2を組み込み

Struts2に必要なライブラリをダウンロードしましょう。
はじめての方は”struts-バージョン-all.zip”となっているファイルをダウンロードしましょう。
スクリーンショット 2019-04-09 21.49.29.png
ダウンロードしたStruts-2.5.20-all.zipを展開しstruts-2.5.20->libから次の13個を

  • commons-fileupload-1.4.jar
  • commons-io-2.6.jar
  • commons-lang-2.4.jar
  • commons-lang3-3.8.jar
  • commons-logging-1.2.jar
  • freemarker-2.3.28.jar
  • javassist-3.20.0-GA.jar
  • log4j-api-2.11.1.jar
  • ognl-3.1.21.jar
  • struts2-core-2.5.20.jar
  • xmlpull-1.1.3.1.jar
  • xpp3_min-1.1.4c.jar
  • xstream-1.4.11.jar

プロジェクトの配下のWebContent->WEB-INF->libにコピーします。
eclipseのプロジェクトエクスプローラー(左の小窓)上の同じ場所のlibをリフレッシュして環境に反映しましょう。
にドラッグ&ドロップしましょう。
スクリーンショット 2019-04-09 22.02.03.png
プロジュエクト名を右クリックして、Javaのビルド・パスから、クラスの作成先のパスを変更します。
スクリーンショット 2019-04-10 7.25.43.png

フォーム画面(index.jsp)

プロジェクトエクスプローラー(左の小窓)のWebContentの上で右クリック
新規->ファイルを押してJSPファイルを作成しましょう。
下記の画面が表示されるのでFile nameに”index.jsp”と入力して”Finish”を押しましょう。
以下のようにフォーム画面(index.jsp)を記述しましょう。

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC 
  "-//W3C//DTD HTML 4.01 Transitional//EN" 
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Hello Struts2!</title>
    </head>
    <body>
        <form action = "HelloStruts2">
            <label for = "name">Please enter your name</label><br/>
            <input type = "text" name = "name"/>
            <input type = "submit" value = "OK"/>
        </form>
    </body>
</html>

結果画面(HelloStruts.jsp)

以下のように結果表示画面(HelloStruts.jsp)を記述しましょう。

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC 
  "-//W3C//DTD HTML 4.01 Transitional//EN" 
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <title>Hello Struts2!</title>
    </head>
    <body>
        Hello Struts2, <s:property value="name"/>
    </body>
</html>

?「<%@ taglib prefix="s" uri="/struts-tags" %>」は、Jspのタグライブラリにページロードされています。

アクションファイルを作成

一見完成したように思えますがまだ未完成です。
なぜなら、フォーム画面から結果画面へ変数(name)を受け渡す必要があるからです。
そこで、変数の受け渡しを行うアクションファイル(HelloStruts2Action.java)を作成します。
HelloStruts2Action.javaをsrcの下にパッケージ名をhelloで作成します。
プロジェクトエクスプローラー(左の小窓)のJava Resources->srcで右クリックしHelloStruts2Action.javaを作ります。
スクリーンショット 2019-04-11 21.41.37.png

以下のようにHelloStruts2Action.javaを記述しましょう。

package com.stuts2.demo;

public class HelloStruts2Action {
    private String name;
    public String execute() throws Exception {
        return "success";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Struts.xmlの作成(Strutsのルール)

「フォームの処理をHelloStruts2Action.excecute()で行い戻り値が”success”だったら”HelloWorld.jsp”(結果画面)を表示する」
ルールを明記する必要があります。
それはstruts2_demo->src->struts.xmlに記述します。
struts.xmlを下記のように記述しましょう。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.devMode" value="true" />
    <package name="Demo" extends="struts-default">
        <action name="HelloStruts2" class="com.stuts2.demo.HelloStruts2Action" method="execute">
        <result name="success">/HelloStruts.jsp</result>
        </action>
    </package>
</struts>

package要素の属性には、以下の値が設定できます

要素 解説
name 複数のアクションをグループ可する際の名前。別のpakage名と重複しないようにする(必須)
extends 別のpackageを継承する。継承することによりそのパッケージの要素にアクセスできるようになる(任意)
namespace urlの一部になる(任意)
abstract trueを設定すると、子要素のactionが必須項目でなくなる(任意)

web.xmlの設定(URLのルーティング)

Strutsは、バージョンによってMVCフレームワークを開発環境に導入実現も違います。今回Struts2.5をロードしますので、
WebContent->WEB-INF->web.xmlに、以下のように内容を編集します。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
    <display-name>struts2_demo</display-name>
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

TomcatにContextを配置

Tomcatサーバのconf/Catalina/localhostディレクトリの下に、struts2_demo.xmlを作ります。
struts2_demo.xmlの中身は以下とないrます。

<Context path="/struts2_demo" docBase="/work/ws_eclipse/struts2_demo/WebContent" reloadable="false" source="org.eclipse.jst.jee.server:struts2_demo" />

eclipseでstruts2アプリを起動

EclipseからTomcatを起動します。
起動するログには、以下のメッセージが確認できれば、struts2_demoが立ち上がりました。
スクリーンショット 2019-04-13 11.44.00.png
画面で動かしましよう。
ブラウザーに、「http://localhost:8080/struts2_demo/index.jsp」を入力すると、以下の画面が表示します。
スクリーンショット 2019-04-13 11.52.24.png
任意文言を書いてOKを押すと、次のページに遷移します。
スクリーンショット 2019-04-13 11.53.55.png

最後に、上記で作られたプロジェクトの構築は以下の通りになりました。
スクリーンショット 2019-04-13 12.02.25.png

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

spring bootをマルチプロジェクトでgradleしてみたかった

はじめに

eclipseでspring-bootでgradleでマルチなプロジェクトでプロジェクトフォルダを同階層に配置した構成でやってみようと思いました。なかなかうまくいかなかったのでメモ代わりに!

構成

fooをルートプロジェクトにしてbarを子プロジェクトとして↓のようにしてみました。

foo ← プロジェクトフォルダ
    build.gradle
    settings.gradle
bar ← プロジェクトフォルダ
    build.gradle

内容

foo

fooフォルダのgradleはいつものようにgradleを作って、settings.gradleの1行目にincludeFlat 'bar'を書き足しました。

foo/build.gradle
plugins {
    id 'org.springframework.boot' version '2.1.4.RELEASE'
    id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    compile project(':bar')
    compileOnly 'org.projectlombok:lombok'
}
foo/settings.gradle
includeFlat 'bar'
/*
pluginManagement {
    repositories {
        gradlePluginPortal()
    }
}
*/
rootProject.name = 'foo'

bar

こっちはいろいろ試して最後に以下のようになりました。
pluginsにid 'org.springframework.boot' version '2.1.4.RELEASE'があるとうまく認識してくれませんでした。
apply plugin: 'io.spring.dependency-management'apply plugin: 'org.springframework.boot'はなくても動くかもしれません。(試してませんw)

bar/build.gradle
plugins {
    id 'java'
}

apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.springframework.boot'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'org.springframework.boot:spring-boot-starter-thymeleaf'
    compileOnly 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    compileOnly 'org.projectlombok:lombok'
}

おわり

とにかくこんな構成で動くところまでは確認しました。
久しぶりにgradle触ったら記述方法が変わってて軽いギャップを感じてしまいました。

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

Downgrade Java MacOS

After install Java check what versions do you have:

$ java --version

Something like this will appear:

Matching Java Virtual Machines (2):
    12, x86_64: "Java SE 12"    /Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home
    11.0.2, x86_64: "Java SE 11.0.2"    /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home

Select the version you want:
/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home

open your bash to edit:

$ sudo vi ~/.bash_profile

and add this line:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home

update your bash:

source ~/.bash_profile

confirm:

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