JavaでSCP

とりあえずやってみたってだけで、あってるかは不明。
ちゃんと使うには、もっと中身を覗いていかないとだめかも。

ライブラリ

ganymed-ssh-2 というのを使ってみる。
https://code.google.com/p/ganymed-ssh-2/

バージョンは(Mavenであがってた)262というもの。

ドキュメントがあまりなさそう(古いのはリソースがけっこうある?)で、妄想だけで作ってくのは辛いです・・・

サンプル

public static void main(String[] args) {
    
    Connection conn = null;
    try  {
        conn = new Connection("xxx.xxx.xxx.xxx");
        conn.connect();
        
        boolean auth = conn.authenticateWithPassword("hoge", "foo");
        if (!auth) {
            System.err.println("auth error.");
            return;
        }
        
        File file = new File("C:/tmp/sample.txt");
        SCPClient scp = conn.createSCPClient();
        
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
            SCPOutputStream out = scp.put(file.getName(), file.length(), "/home/hoge/", null);) {
            IOUtils.copy(in, out); // Commons IOを使う手抜き
        } 
        
    } catch(IOException e) {
        e.printStackTrace();
    } finally {
        if (conn != null) {
            conn.close();
        }
    }
}

ConnectionクラスはAutoCloseableを実装してない・・・Java7からだしね・・・

Spring Batchでエラー時だけ特定の処理をする設定

こんな感じ?

<batch:job id="testJob">
	<batch:step id="test1">
		<batch:tasklet ref="hogeTasklet" />
		<batch:end on="*" />
		<!-- 次のstepがある場合は以下
		<batch:next on="*" to="test2" />
		-->
		<batch:next on="FAILED" to="errorStep" />
	</batch:step>
	<batch:step id="errorStep">
		<batch:tasklet ref="errorTasklet" />
	</batch:step>
</batch:job>

でも、今後stepが増えた際に、すべてのところでこういう風に書くのめんどいような。
StepListener使ったほうがいいのかなー。

ジェネリクスむずかしい

ちょっと悩んだメモ。

あくまでイメージコード

public void hoge1(List<? extends Serializable> list) {
	// List<List<? extends Serializable> では受け取れない
	List<?> list2 = foo(list);
}

public void hoge2(List<String> list) {
	List<List<String>> list2 = foo(list);
}

public <T extends Serializable> void hoge3(List<T> list) {
	List<List<T>> foo = foo(list);
}

public <T> List<List<T>> foo(List<T> list) {
	List<List<T>> result = new ArrayList<>();
	result.add(list);
	return result;
}

何を悩んだのかというと

インタフェースの定義としてどちらにしておくべきなのか?

public interface Foo {
	void hoge(List<? extends Serializable> list);
	<T extends Serializable> void hoge(List<T> list);
}

使う側からすれば変わらないような気がするけど・・・

これだと使う側に影響はある

なので意味はある。

public interface Foo {
	List<? extends Serializable> bar1(List<? extends Serializable> list);
	<T extends Serializable> List<T> bar2(List<T> list);
}
public void use() {
	Foo foo = ・・・
	List<String> list = new ArrayList<>();
	
	// List<String>で受けられない
	List<? extends Serializable> bar1 = foo.bar1(list);
	// List<String>で受けられる
	List<String> bar2 = foo.bar2(list);
}

bar1は、引数の?と戻り値の?が一緒の型であるって定義はしていない・・・から?

riak-java-clientを使う

RiakにJavaからアクセスするためのライブラリ https://github.com/basho/riak-java-client
をちょっと触ったのでメモ。
LowレベルとHighレベルの2種類のAPIがあるけど、今回は手軽に試せるHighレベルの方で。

つっこむデータの定義

アノテーションで色々指定できる。
IDはアノテーションでなくても指定するインタフェースがあるけど、とりあえずお試しなので。
デフォだとJSON形式で保存されて、マッピングなどは勝手にやってくれる。
基本的な型だったりネストとかしても、とりあえず大丈夫。

public class User {
    @RiakKey // Key
    public String id;
    
    @RiakIndex(name = "age") // Secondary Index
    public long age;
    
    @RiakLinks // Link
    public Collection<RiakLink> links;
}

処理

色々とはしょってるけど、まぁイメージってことで。

IRiakClient client = null;
try {
    client = RiakFactory.pbcClient("host", port);
    Bucket bucket = client.fetchBucket("test").execute();
    
    User user = new User();
    // ・・・略
    bucket.store(user).execute();
    
    // 1件取得
    User fetchUser = bucket.fetch("1000", User.class).execute();
    // 複数件取得
    List<MultiFetchFuture<User>> list = bucket.multiFetch(Arrays.asList(new String[]{"1000", "1001"}), User.class).execute();
    // indexで取得
    List<String> ids = bucket.fetchIndex(IntIndex.named("age_int")).from(30).to(50).execute();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    client.shutdown();
}

というわけで

  • けっこう簡単に使えていい感じ。マッピングとか勝手にやってくれて素敵。
  • IRiakClientは内部でPoolを持ってたりして設定などでPool数などを変更できる。

HiveのmetastoreにPostgreSQLを使う

とりあえず試しにやってみただけメモ。
よくわからずやってるので、無駄なものとかあるかも。


手順

  • PostgreSQLを入れて、よしなに設定する。
  • Hiveについてくるscripts/metastore/upgrade/hive-schema-x.x.x.postgres.sqlを流す。
  • hive/lib にPostgreSQLJDBCドライバのjarファイルを入れる。
  • hive-site.xmlを修正する。
<property>
  <name>hive.metastore.local</name>
  <value>true</value>
</property>
<property>
  <name>javax.jdo.option.ConnectionURL</name>
  <value>jdbc:postgresql://xxx:5432/xxx</value>
</property>
<property>
  <name>javax.jdo.option.ConnectionDriverName</name>
  <value>org.postgresql.Driver</value>
</property>
<property>
  <name>javax.jdo.option.ConnectionUserName</name>
  <value>xxx</value>
</property>
<property>
  <name>javax.jdo.option.ConnectionPassword</name>
  <value>xxx</value>
</property>

で、、、

なんとなく設定できたみたいなんだけど、hive上で「show tables;」とやるとエラーが出る。
create tableやselectなどは問題なし。

エラーはこんな感じ

 ERROR exec.FetchTask (SessionState.java:printError(365)) - FAILED: Error in metadata: MetaException(message:Got exception: javax.jdo.JDODataStoreException Error executing JDOQL query "SELECT "THIS"."TBL_NAME" AS NUCORDER0 FROM "TBLS" "THIS" LEFT OUTER JOIN "DBS" "THIS_DATABASE_NAME" ON "THIS"."DB_ID" = "THIS_DATABASE_NAME"."DB_ID" WHERE "THIS_DATABASE_NAME"."NAME" = ? AND (LOWER("THIS"."TBL_NAME") LIKE ? ESCAPE '\\' ) ORDER BY NUCORDER0 " : ERROR: エスケープシーケンスが無効です
  ヒント: エスケープ文字は空か1文字でなければなりません。.)
 org.apache.hadoop.hive.ql.metadata.HiveException: MetaException(message:Got exception: javax.jdo.JDODataStoreException Error executing JDOQL query "SELECT "THIS"."TBL_NAME" AS NUCORDER0 FROM "TBLS" "THIS" LEFT OUTER JOIN "DBS" "THIS_DATABASE_NAME" ON "THIS"."DB_ID" = "THIS_DATABASE_NAME"."DB_ID" WHERE "THIS_DATABASE_NAME"."NAME" = ? AND (LOWER("THIS"."TBL_NAME") LIKE ? ESCAPE '\\' ) ORDER BY NUCORDER0 " : ERROR: エスケープシーケンスが無効です
  ヒント: エスケープ文字は空か1文字でなければなりません。.)


原因は、PostgreSQL 9.1からstandard_conforming_stringsの設定がoffからonに変わったためのよう。
http://www.postgresql.org/docs/9.1/static/release-9-1.html


postgresql.conf を変更したらエラーは出ないようになったけど、色々と不安だらけだ・・・

今が年の何週目か

ある日がその年の何週目かということについて。
というよりかは、年の1週目っていつよ?ってお話。


まずはISO8601の定義。

  • D は曜日を表し、月曜日が 1、日曜日は 7 である。

〜略〜

  • ある年における、「最初の木曜日を含む週が、その年の第1週である。」と規定されている。

これはJIS X 0301にも例示されている通り、

  • 「第1週は事実上、1月4日を含む週である。」という基準認識に等しい。
  • 年初において以下の曜日に該当する場合、その日は新年の曜日としては扱わず、あくまで旧年最終週の曜日として扱う、という事を意味する。
    • 1月1日金曜日・1月2日土曜日・1月3日日曜日

・・・
http://ja.wikipedia.org/wiki/ISO_8601

曜日は月曜日からで、1/4を含む週が1週目になるとのこと。
で、Javaのドキュメント。

WEEK_OF_YEAR フィールドに対して計算される値の範囲は、1 から 53 です。年の第 1 週は、その年から少なくとも getMinimalDaysInFirstWeek() 日を含む getFirstDayOfWeek() で始まるもっとも早い 7 日間です。このように第 1 週は getMinimalDaysInFirstWeek()、getFirstDayOfWeek() の値、および 1 月 1 日の曜日により変化します。ある年の第 1 週から翌年の第 1 週までの週は、必要に応じて 2 から 52 または 53 まで順に番号が付けられます。
http://java.sun.com/javase/ja/6/docs/ja/api/java/util/GregorianCalendar.html


とうわけで、このようにする必要がある?

Calendar calendar = new GregorianCalendar();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);

calendar.set(2010, Calendar.JANUARY, 1);
System.out.println(calendar.get(Calendar.WEEK_OF_YEAR));
→53


めんどくさい・・・