問題
java.io.NotSerializableException
とあるオブジェクトをこのtoByte()でbyteに変換しようとしたらエラーがでた。
Javaで大量データをメモリに展開するテクニックの考察 - Symfoware
解決方法
import java.io.Serializable; public class TransientExample implements Serializable { private static final long serialVersionUID = 6128016096756071380L; private transient Pair pair = null; public TransientExample(String key, Integer value) { this.pair = new Pair(key, value); } @Override public String toString() { return pair.toString(); } }
適当なクラスを作って、
・implements Serializableつける
・問題のあるクラスをtransient で定義しなおす
でエラーきえた。
~~~
あれ、逆か。transientは不要っぽい。
それと、そもそもOAuthConsumerをSerializeしようとしていてエラーが発生したのがきっかけだが、少し複雑な感じ。
結論からいくと、OAuthConsumerはサーバ側で保持する必要はなく、Providerだけ保持していればいいっぽい。
Issue 11 - oauth-signpost - Make OAuthProvider and OAuthConsumer serializable - Simple OAuth message signing for Java - Google Project Hosting
そして、OAuthConsumerはnewで初期化した時点ではserialize可能だが、たぶんproviderと混ぜてURL生成なんかをしていると、途中からserializeできなくなる。(しようとするとnullが返された。serializeするライブラリによってはエラーを吐く)
url = provider.retrieveRequestToken(consumer, callback);
~~~
最終的にうまくいったのでまとめ。
oauthの各ステップ
- STEP01:認証URLの生成
- consumerとproviderを初期化
- sessionやDBとかでproviderは保持しておく
- consumerが持っているaccessTokenKeyとSecペアをsessionやDBで保持しておく(これがわかりにくかった)
- STEP02:認証ページからのCALLBACK
- callback先に認証キーがGETパラメータで返ってくるので、それをsessionとかDBで保持しておく
- STEP03:利用
- providerは保持しておいたやつをそのまま使う
- consumerはSTEP01と同様に初期化した後、STEP01で保持しておいたaccessTokenKeyとSecをconsumerに適用する(これがわかりにくかった)
- providerにconsumerとSTEP02で得た認証キーを適用する
- 後は公式通りに利用
公式には
consumer.setTokenWithSecret(ACCESS_TOKEN, TOKEN_SECRET);
とだけあり、ACCESS_TOKENやTOKEN_SECRETは、callbackで得られるkeyとsecのペアのことかと思っていた。
しかし、これはSTEP01でcosumerが持っているやつを使わなければいけない。
STEP01
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX"); OAuthProvider provider = new DefaultOAuthProvider("XXX","XXX"); authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL); // accessTokenをsessionやDBに保持 ↓こいつら System.out.println(consumer.getToken()); System.out.println(consumer.getTokenSecret()); //...
STEP02
//認証元からGETで受け取った認証キーをsessionやDBに保持 //...
STEP03
OAuthProvider provider = (OAuthProvider)session.getAttribute("provider"); OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX"); consumer.setTokenWithSecret("STEP01のaccessTokenKey", "STEP01のaccessTokenSec"); provider.retrieveAccessToken(consumer, "STEP02で認証元からうけとった認証キー"); //あとは普通通り URL url = new URL("認証が必要なリソースURL"); //...略
~~~
さらにハマった。。
上の手順だとSTEP03を2回以上くりかえすと、2回目からは401がでてしまう。
理由は、retrieveAccessTokenするとaccessTokenとaccessTokenSecretが更新されるから。
なので、
STEP02でretrieveAccessTokenを済ませて置き、そこで新しく発行されるtokenを保持し、STEP03で何度も同じものを使うというふうにしなければいけない。
STEP01
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX"); OAuthProvider provider = new DefaultOAuthProvider("XXX","XXX"); authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL); // accessTokenをsessionやDBに保持 ↓こいつら System.out.println(consumer.getToken()); System.out.println(consumer.getTokenSecret()); //... // providerもsessionなんかで保持しておく session.setAttribute("provider", provider);
STEP02 (retrieveAccessTokenを済ませて置き、そこで新しく発行されるtokenを保持)
OAuthProvider provider = (OAuthProvider)session.getAttribute("provider"); OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX"); provider.retrieveAccessToken(consumer, "GETパラメータでうけとった認証キー"); // ここで実はconsumerのaccesstokenが更新されている。 // のでそいつらを保持しておく System.out.println(consumer.getToken()); System.out.println(consumer.getTokenSecret()); //... // providerはもう用済みなので消してよい
STEP03
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX"); consumer.setTokenWithSecret("STEP02のaccessTokenKey", "STEP02のaccessTokenSec"); //あとは普通通り URL url = new URL("認証が必要なリソースURL"); //...略