2012年5月30日水曜日

ScalaからAWS SDK for Javaを使ってDynamoDBをいじったTips

AWS SDK for Javaのバージョンは、1.3.10です。
使ってみた時に詰まった点をメモってみました。


1. ResourceNotFoundExceptionが出る原因


以下のような例外が発生する場合の原因と対処法です。

[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException, AWS Error Message: Requested resource not found
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException,AWS Error Message: Requested resource not found

原因1 Endpointが設定されていない

val client : AmazonDynamoDBClient = ...// init
client.setEndpont("http://dynamodb.ap-northeast-1.amazonaws.com"); // <- 東京RegionのEndpoint設定。
自分のDynamoDBを使用しているRegionを設定しましょう。Endpoingの一覧はこちら

原因2 Tableが作成されていない

指定したTableが存在しない場合にも同じ例外が発生します。Tableが正常に作成されているかと、プログラム中でTable名の指定が間違ってないかを確認しましょう。
Tableがまだ作成されていない場合は、ManagedConsoleから作成してあげましょう。


2. ValidationExceptionが出る原因


以下のような例外が発生する場合の原因と対処法です。
[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema

原因1 HashKeyまたはRangeKeyの型が違う


HashKeyとRangeKeyの型が間違っている場合に良く起こります。 Table定義を確認して、StringとNumber型の指定が正しいことを確認してください。

new AttributeValue().withS("hogehoge") //Table定義ではNumberになっている場合コレが原因になる



3. O/R mapperを使ってRangeKeyにDate型を指定したけど、queryで上手くフィルターできない


O/R mapperでDateを使用する場合は、DynamoDB上ではISO8601の日付フォーマットの文字列として保存されます。
import com.amazonaws.util.DateUtils

DateUtils dateUtils = new DateUtils();

dateUtils.formatIso8601Date(date)
dateUtils.parseIso8601Date(dateString)

で、変換できます。queryやscan時のConditionで指定する場合は、このメソッドを使いましょう。


4. ScalaからAWS SDKのO/R mapperを使う


Java Beansにしか対応していないので、通常のScalaPlainObjectでは上手く認識してくれません。
基本的には、@BeansInfoアノテーションをつければ、getter/setterを生成してくれるので変数の定義に関しては問題ありません。
しかし、varにつけたアノテーションは、コンパイルされるタイミングで、同名のprivate fieldのほうに付けれられてしまいます。
そのため、AWS SDKのアノテーションが有効にならないので、地道に手作業でgetter/setterを作成してアノテーションをつけないと行けません。
また、case classにしておくと勝手にequalsやhashCode関数をオーバーライドしてくれるので便利ですが、引数なしのコンストラクタもあわせて定義しておいてあげましょう。

NG

import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._

case class Hoge( @BeansInfo @DynamoDBHashKey(attributeName="Id") var id : Long,
 @BeansInfo var nickname : String){
  def this() = this(0,"")
}

OK

import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._

case class Hoge(var id : Long,
 @BeansInfo var nickname : String){
  def this() = this(0,"")

  @DynamoDBHashKey(attributeName="Id")
  def getId() : Long = id
  def setId(id : Long) = this.id = id;
}



5. O/R Mapper時にクラスの継承を行うと変数が上手くマップされない


幾つかのテーブルが同じようなテーブルで同じ変数持ってるから、継承で共通化しちゃえと思って継承を使ってみたところ、上手くO/R Mapperがフィールドをマップしてくれませんでした。
回避方法は、「継承元のクラスにDynamoDBTableアノテーションを付ける」になります。

NG

class Hoge(... , var hoge : String){
  def this() = this(...) 
  ...
   def getHoge():String = hoge
   def setHoge( hoge : String){ this.hoge = hoge}
}
@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge

OK

@DynamoDBTable(tableName="DummyAnnotation") //<- これ重要
class Hoge ...


@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge

ちなみに、こうなっちゃう原因はDynamoDBRefrector@isRelevantGetter(DynamoDBRefrector.java:92)の
private boolean isRelevantGetter(Method m) {
    return (m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getParameterTypes().length == 0
        && m.getDeclaringClass().getAnnotation(DynamoDBTable.class) != null
        && !m.isAnnotationPresent(DynamoDBIgnore.class);
}
で、メソッドのDeclaringClassのアノテーションを取ってしまっちゃってるからです。

2012年5月11日金曜日

pythonでファイルやディレクトリを列挙 まとめ

いつもファイルとかディレクトリを列挙しようとするたびにググり続けているので、自分でまとめてみた。


指定したディレクトリのすべてのファイルとディレクトリを列挙

import os

for onlyFilename in os.listdir("mydir"):
    print onlyFilename

--------------------
'dir1'
'dir2'
'file1.txt'
'file2.txt'
'file2.py'
常にファイル、ディレクトリ名だけを返します。

特定の拡張子のファイルだけを列挙

import glob

for filenameWithPathAsString in glob.glob("mytexts/*.txt"):
    print absFilePathAsString

--------------------
'mytexts/hoge.txt'
'mytexts/wahoo.txt'
...

出力には、指定したPathも付けられています

ディレクトリのみを列挙

import glob
import os
import os.path

for filename in glob.glob("mydir/*"):
    if os.path.isdir(filename):
        print filename

--------------------
'mydir/dir1'
'mydir/dir2'
...



再帰的に全てのファイル、ディレクトリを列挙

for path,dirs,files in os.walk("mydir"):
  print path,dirs,files
深さ優先でディレクトリを掘って行きます。 例
"." -> "./dir1" -> "./dir1/hoge" -> "./dir2" -> "./dir2/hoge"

2012年4月24日火曜日

specs2で並列テストをOffにする

ScalaのBDDテストフレームワークのspecs2に関するTips
ありがた迷惑な機能として、specs2ではテスト実行時に自動で並列実行してくれます。
テスト実行は早くなりますが、DBの絡んだテストやファイルの絡んだテストでは問題になることも多々あります。
ただし、次のようにするだけで簡単に逐次実行に変更可能なので並列テストでの問題が出た場合は試してみてください。


import scala.specs2.Specification
// or
import scala.specs2.mutable.Specification

class MediaProxyTest extends Specification{
  
  sequencial // <- この1行を付けるだけ

  /* test codes +/


}

2012年2月22日水曜日

giter8でprivate repositoryにアクセス

Readme腐ってる

Linux,Macなら、
http://help.github.com/set-your-user-name-email-and-github-token/
の通りに設定すればOK

Windows環境はgit.exeにパス通していれば、同じ設定を行えば良いが、
そうでない場合は
GITHUB_USERとGITHUB_TOKENの環境変数を定義してあげればOK

2012年2月14日火曜日

scalaで簡単Proxyクラス実装。

ProxyデザインパターンやDecoratorデザインパターンを使いたいことがよくあると思いますが、いちいちすべてのメソッドを実装するのはめんどくさいですよね?
そんな時に便利なscalaの実装を紹介


class User(var id : Long,var name : String,var gender : Int)

class ProxyUser(private val user : User){
def name : String = if(gender == 1) "Mr. " + user.name else "Ms. " + user.name
}

object ProxyUser{
implicit def toUser(proxyUser : ProxyUser) = proxyUser.user
}



Companion objectにimplicit conversionを指定しておくだけで完了。
あとは、ProxyしたりDecorateしたりしたいメソッドを定義してやるだけでOK。

使い方はこんな感じ

val pu = new ProxyUser(new User(1,"Taro",1))

println("ID:" + pu.id)
println("name:" + pu.name)
pu.name = "Joro"
println("name:" + pu.name)

> ID:1
> name:Mr. Taro
> name:Mr. Jiro

2012年2月13日月曜日

Squerylでunion

scalaのO/R mapperのSquerylでunionを行う方法

複合Keyとかでin句が使えない場合のunion構文の実装方法のメモ

Queryクラスのunionメソッドを使います。



val table = Table[CompositeKey2Object]

val ids = List( (1,2),(1,3),(2,5) )

inTransaction{
def toQuery( id : (Int,Int) ) = {
from(table)(t => where( (t.pk1 === id._1) and (t.pk2 === id._2)))
}
ids match{
case id :: tails => {
ids.foldLeft(toQuery(id))( (query,id) => query.union(toQuery(id))).toList
}
case _ => Nil
}
}

2012年1月23日月曜日

JDBCで使えるURL一覧

JDBCに指定するURLとDriverClassのまとめ




DB名DriverClass種別url
MySQLcom.mysql.jdbc.Driverサーバーjdbc:mysql://[server_name]:[port=3306]/[database_name]
H2DBorg.h2.Driverサーバーjdbc:h2:tcp://[server]:[port]/[path]/[database_name]
ファイルjdbc:h2:file:[path][database_name]
メモリjdbc:h2:mem:[database_name]
Oracleoracle.jdbc.driver.OracleDriverサーバーjdbc:oracle:thin:@[server_name]:[port=1521]:[database_name]
Microsoft SQL servercom.microsoft.jdbc.sqlserver.SQLServerDriveサーバーjdbc:microsoft:sqlserver://[server_name]:[port=1433]/[database_name]; DatabaseName=[database_name]
Derbyorg.apache.derby.jdbc.EmbeddedDriverファイルjdbc:derby:[path]/[database_name];create=true
メモリjdbc:derby:memory:[database_name]
PostgreSQLorg.postgresql.Driverサーバーjdbc:postgresql://[server_name]:[port=5432]/[database_name]
Sqliteorg.sqlite.JDBCファイルjdbc:sqlite:[path]/[database_name]
org.sqlite.JDBC