日曜日, 1月 18, 2015

guice

現在のプロジェクトからguiceを使うことになったが,これはなかなかの優れものだと思う.
今まで使ったDIフレームワークと比べても,コードを簡潔に短くことが出来るようになったと思う.
  以下の例はFTPでファイルを送信するロジックを仮実装した例.コンフィグのバインドの例は決して真似をせぬ様.あくまでguiceの基本バインドの例である.


FileTransmit.java
ackage org.tanuneko;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;


public class FileTransmit {

    public static void main(String args[]) {

        String[] myArgs = {"/home/neko32", "tako.log", "640"};
        Config c = new AppConfig(myArgs);

        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(FileSenderIF.class).to(FTPFileSender.class);
                bind(Config.class).toInstance(c);
            }
        });

        FileTransmitProgram ft = injector.getInstance(FileTransmitProgram.class);
        ft.run();
    }

    class MyAppConfig extends Config {
        MyAppConfig(String[] args) {
            // do not do this in product code
            super.logFileLocation = args[0];
            super.logFileName = args[1];
            super.maxFileSize = Integer.parseInt(args[2]);
        }
    }
}



FileTransmitProgram.java
package org.tanuneko;

import com.google.inject.Inject;

public class FileTransmitProgram {

    @Inject
    private FileSenderIF sender;
    @Inject
    private Config conf;


    public void run() {
        sender.setLogging(conf.getLogFileLocation(), conf.getLogFileName());
        sender.setFileMaxFileSize(conf.getMaxFileSize());
        sender.send("ABC", "DATA");
    }
}


FTPFileSender.java
package org.tanuneko;

public class FTPFileSender implements FileSenderIF {

    private int maxFileSize = -1;

    public void setLogging(String logLocation, String fileName) {
        System.out.println(String.format("Setting logs[%s/%s]", logLocation, fileName));
    }

    public void setFileMaxFileSize(int maxFileSize) {
        int prevMax = maxFileSize;
        this.maxFileSize = maxFileSize;
        System.out.println(String.format("max file size has changed from %d to %d", prevMax, this.maxFileSize));
    }

    @Override
    public void send(String fileName, String data) {
        System.out.println(String.format("file %s(%s) was sent via FTP", fileName, data));
    }
}

日曜日, 1月 11, 2015

Java8 - Function::compose

Java8のFunctionクラスのcompose()メソッドを使えば,簡単にメソッドチェインを実装できる.
上手く使えばコードをかなり簡潔にできるだろう.以下のコードは3つの文字列操作フィルタをチェインして適用させた例.

package org.tanuneko;

import java.util.function.Function;
import java.util.stream.Stream;

public class FuncComposer {

    public FuncComposer() {
    }

    public void run() throws Exception {
        Function<String,String> addPrefixFilter = x -> "***" + x;
        Function<String,String> addPostfixFilter = x -> x + "***";
        Function<String,String> toLowerCaseFilter = x -> x.toLowerCase();
        String base = "WOW";

        Function<String,String> composedFilter =
        Stream.of(addPrefixFilter, addPostfixFilter, toLowerCaseFilter)
                .reduce((func, nxt)->func.compose(nxt))
                .orElseGet(Function::identity);

        System.out.println(composedFilter.apply(base));
    }


    public static void main(String args[]) throws Exception {
        FuncComposer c = new FuncComposer();
        c.run();
    }
}

ElasticSearch IntegrationTest

ElasticSearch IntegrationTestを使えば,メモリ上での仮想クラスタ上で単体テストを走らせることができる優れものだが,あまりサンプルコードが見つからない気がする.


package org.tanuneko;

import org.apache.commons.io.FileUtils;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.junit.Test;

import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.function.Function;

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.core.Is.is;


@ClusterScope(scope= ElasticsearchIntegrationTest.Scope.SUITE,numDataNodes=2)
public class ESIntegrationTest extends ElasticsearchIntegrationTest {

    private static final String INDEX_NAME = "tanuapp";
    private static final String DOCTYPE_NAME = "posting_msg";
    private static final String TODAY_DATE_STR = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

    @Test
    public void testSearch() throws Exception {
        createIndex(INDEX_NAME);
        client().admin().indices().preparePutMapping(INDEX_NAME)
                .setType(DOCTYPE_NAME)
                .setSource(readMappingFile(DOCTYPE_NAME + "_mappings.json"))
                .execute().actionGet();

        assertThat(indexDoc(INDEX_NAME, DOCTYPE_NAME, "1", "neko32", "hellou").isCreated(), is(true));
        assertThat(indexDoc(INDEX_NAME, DOCTYPE_NAME, "2", "ushi", "poooo").isCreated(), is(true));
        assertThat(indexDoc(INDEX_NAME, DOCTYPE_NAME, "3", "neko64", "ka-kakinkin ka-kinkin").isCreated(), is(true));

        flushAndRefresh(INDEX_NAME);
        ensureGreen(INDEX_NAME);
        assertThat(indexExists(INDEX_NAME), is(true));

        GetResponse resp = client().prepareGet(INDEX_NAME, DOCTYPE_NAME, "3")
                .execute()
                .actionGet();

        Map source = resp.getSource();
        assertThat((String)source.get("user"), is("neko64"));
        assertThat((String)source.get("message"), is("ka-kakinkin ka-kinkin"));
        final String postedDate = (String)source.get("postDate");
        Function isSameDate = x -> x.startsWith(TODAY_DATE_STR);
        assertTrue(isSameDate.apply(convertToGMT(postedDate)).booleanValue());

        SearchResponse searchResp = client().prepareSearch(INDEX_NAME)
                .setTypes(DOCTYPE_NAME)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(QueryBuilders.fuzzyLikeThisFieldQuery("user").likeText("neko50"))
                .setPostFilter(FilterBuilders.idsFilter(DOCTYPE_NAME).ids("1"))
                .execute()
                .actionGet();

        assertThat(searchResp.getHits().getHits().length, is(1));
        assertThat(searchResp.getHits().getAt(0).getSource().get("user"), is("neko32"));
    }


    private String readMappingFile(final String fileName) throws Exception {
        return FileUtils.readFileToString(new File(ESIntegrationTest.class.getClassLoader().getResource(fileName).getPath()));
    }

    private String convertToGMT(String dateStr) {
        return LocalDateTime.parse(dateStr.substring(0,dateStr.length()-1)).toString();
    }

    private IndexResponse indexDoc(final String idxName,
                                    final String docName,
                                    final String id,
                                    final String user,
                                    final String msg) throws Exception {
        return client().prepareIndex(INDEX_NAME, DOCTYPE_NAME)
                .setSource(genMsg(user, msg))
                .setId(id)
                .execute().actionGet();
    }

    private XContentBuilder genMsg(String user, String msg) throws Exception {
        return jsonBuilder().startObject()
                .field("user", user)
                .field("postDate", new Date())
                .field("message", msg)
                .endObject();
    }
}