これでいいのかしら。。


    public static Key allocateId(AsyncDatastoreService ds, String kind)
            throws NullPointerException {
        if (ds == null) {
            throw new NullPointerException("The ds parameter must not be null.");
        }
        if (kind == null) {
            throw new NullPointerException(
                "The kind parameter must not be null.");
        }
        String ns = NamespaceManager.get();
        if (ns == null) {
        	ns = "";
        }
        String cacheKey = "!"+ns+":"+kind;
        Iterator<Key> keys = keysCache.get(cacheKey);
        if (keys != null && keys.hasNext()) {
            return keys.next();
        }
        keys =
            FutureUtil
                .getQuietly(allocateIdsAsync(ds, kind, KEY_CACHE_SIZE))
                .iterator();
        keysCache.put(cacheKey, keys);
        return keys.next();
    }

Comments [appengine][slim3] DatastoreUtil#allocateIdがNamespaceに対応するように修正 はコメントを受け付けていません


listプロパティにInMemoryInCriterionを適用するとエラーになるので修正。
これでいいのかしら。。


   public boolean accept(Object model) {
        Object v = convertValueForDatastore(attributeMeta.getValue(model));
        for (Object o : value) {
        	if (v instanceof Collection<?>) {
        		for (Object v2: (Collection<?>)v) {
                    if (compareValue(v2, o) == 0) {
                        return true;
                    }
        		}
        	} else {
        		if (compareValue(v, o) == 0) {
        			return true;
        		}
        	}
        }
        return false;
    }

Comments [appengine][slim3]InMemoryInCriterionがlistプロパティでも動作するように修正 はコメントを受け付けていません


忘れがちなのでメモ。


cd appengine-java-sdk-xxx\bin
appcfg.cmd vacuum_indexes c:\path\to\eclipse\project\war

Comments [appengine]不要なインデックスを削除するコマンド はコメントを受け付けていません


Directory APIで 2-legged OAuth を使って認証

Google Appsのユーザー情報を同期しているappengineのアプリケーションでProvisioning APIを使用していましたが、deprecatedになっていました。(2015年で終了。。)
今後は Admin SDK Directory API を使用しろとのことなので、2-legged OAuthを使った使用方法を調査していましたがなかなか情報が見つからずに苦戦。。
試行錯誤の末ようやく成功したのでまとめてみます。

Directory API は ユーザーやグループのUniqueIDも取得できるので、メールアドレスの変更などにも追随できて管理がより楽になります。

サンプルコードの実行条件

  • appengine/java で GoogleAppsと連携するアプリケーション
  • Google Apps Marketplace に登録してある
  • AppsにはMarketplaceからインストールしてある

認証にはMarketplaceのCONSUMER_KEYとCONSUMER_SECRETを使用します。
またAppsの管理者権限があるID(メールアドレス)が必要になります。

ApplicationManifestに追加するスコープ

Google Apps Marketplaceの ApplicationManifestに以下のスコープが必要になります。


    <Scope id="userProvisioningAPI">
        <Url>https://www.googleapis.com/auth/admin.directory.user.readonly</Url>
        <Reason>This app displays all members in domain.</Reason>
    </Scope>
    <Scope id="groupProvisioningAPI">
        <Url>https://www.googleapis.com/auth/admin.directory.group.readonly</Url>
        <Reason>This app displays all groups in domain.</Reason>
    </Scope>

サンプルコード

まずはOAuthパラメータをセットしてDirectoryAPIインスタンスをビルドします。
2-legged OAuth は1.0にしか対応していないようで、API_KEYも使います。



        NetHttpTransport TRANSPORT = new NetHttpTransport();
        JacksonFactory JSON_FACTORY = new JacksonFactory();

        // The 2-LO authorization section
        OAuthHmacSigner signer = new OAuthHmacSigner();
        signer.clientSharedSecret = CONSUMER_SECRET;
     
        final OAuthParameters oauthParameters = new OAuthParameters();
        oauthParameters.version = "1";
        oauthParameters.consumerKey = CONSUMER_KEY;
        oauthParameters.signer = signer;
        //oauthParameters.signRequestsUsingAuthorizationHeader(transport);
        
        // Directory API構築
        directory = new Directory.Builder(
            TRANSPORT, JSON_FACTORY, null)
        .setApplicationName(APP_NAME)
        .setDirectoryRequestInitializer(new DirectoryRequestInitializer(API_KEY))
        .setHttpRequestInitializer(oauthParameters)
        .build();
        

ユーザー一覧の取得例です。customKeysにadminアカウントをセットします。


        customKeys = new ArrayMap<String,Object>();
        customKeys.add("xoauth_requestor_id", ADMIN_ACCOUNT);

        Directory.Users.List list = directory.users().list();
        list.setUnknownKeys(customKeys);
        list.setCustomer("my_customer");
        list.setMaxResults(USER_PAGE_MAX);

        List<User> result = new ArrayList<User>();
        for(;;) {
            Users users = list.execute();
            for (com.google.api.services.admin.directory.model.User u: users.getUsers()) {
                result.add(u);
            }
            if (users.getNextPageToken() != null) {
                list.setPageToken(users.getNextPageToken());
                continue;
            }
            break;
        }

setCustomer(“my_customer”); とすると管理下の全てのユーザーが取得できる。
次のページが有るかどうかは、nextPageTokenで判定。

OAuth 2.0 には対応していないようなんですが、どうなんでしょうか。少なくとも今のところ2.0でのやり方を見つけられていません。

Comments Provisioning APIがdeprecatedになったのでDirectory APIに移行してみた はコメントを受け付けていません



appengineのSearch Service(full-text search) はまだexperimentalですが非常に魅力的な機能です。
正式リリースされればデータの種類によってはDatastoreの代わりに使用することもできそう。最大の特徴は検索の柔軟性にあるでしょうか。
ただし使ってみていくつか不思議な仕様に気づいたので注意が必要。簡単にまとめてみます。

DATE型は時刻を持てない

DATE: a date with no time component とドキュメントにあるようにあくまで日付だけしか持てないようです。
timestampを管理したい場合などはちょっと工夫が必要ですね。
自分はNUMBER型を使用して、特定日時(例えば2010-01-01 00:00:00)からの秒数で保持するようにしました。
※NUMBER型の範囲は -2,147,483,647 ~ 2,147,483,647

DATE型はSDKにバグ?

eclipseの環境でtime部分をクリアしたDateをこのフィールドに渡しても、= のクエリーでヒットしません。
例えば今日が 2013-04-24 だとして、 new Date() をセットすると、date_field = 2013-04-24 でも date_field >= 2013-04-24 でもヒットしません。
production環境ではtime部分をクリアしてもしなくても = でヒットします。

TEXT型は同一フィールド名で複数の値を保持可能

addFieldを繰り返して同じフィールド名に異なる値を追加できるようです。


Document.newBuilder()
    .addField(Field.newBuilder().setName("text1").setText("hoge")
    .addField(Field.newBuilder().setName("text1").setText("fuga")
    .build();

HTML型も可能でしたがNUMBER型はExceptionが発生しました。

TEXT型に = でクエリーした場合、どういう条件でヒットする?

Numeric operators only match against numeric and date fields. と書いてありますので当然かもしれませんが、TEXT型に = を指定したクエリーを実行しても完全一致にはなりませんでした。
例えば、text_field = hoge というクエリーを実行した場合に

  • “hoge_2” はヒットしません。
  • “this is hoge” はヒットします。
  • “@hoge” はヒットします。

という結果でした。
さらにSDKでは全然結果が異なりました。。(これもバグ??)

完全一致が使えると(全文検索以外に)使い途が広がるのですが、文字種を限定するなど工夫が必要そうです。

とりあえず気づいたのはこんなところですがまだまだ落とし穴があるかもしれません。

Comments App Engine Search (Full text search) の不思議な仕様 はコメントを受け付けていません