私はこれと1か月以上戦っており、最後に到達しました. ユースケースは次のとおりです。
1 - クライアントの 1 人に新しいファイルがある場合、クライアントはそれを自分の Google ドライブ アカウントのフォルダに置きます。そのフォルダーは彼のクライアントと共有されています。Google には、共有フォルダ内のファイルが変更されたことを自動的に通知するメカニズムがないため、通知を作成する必要がありました。そのため、通知ボタンを備えた GAE/GWT アプリがあります。そのアプリにはクライアント フォルダーをスキャンするボタンがあり、そのフォルダーに新しいファイルがある場合は、そのフォルダーを共有しているユーザーのメール アドレスを取得し、新しいファイルがあることを知らせるメールを送信します。
2 - 定期的に、すべてのクライアントに大量の電子メールを送信する必要があります。彼のダッシュボードには 2 番目のボタンがあり、メールをフォーマットしてボタンをクリックすると、スキャナーがすべてのクライアント フォルダーに移動し、フォルダーが共有されているすべてのユーザーのリストをコンパイルします。次に、リストをループしてメールを送信します。
Drive SDK の部分は簡単でした。フォルダーを共有している相手は利用できるが、メールは利用できないという壁にぶつかるまでは。そのため、メール アドレスを取得できるように、古い API の DocsService を Drive SDK と組み合わせて使用することを試みました。サンプル アプリケーションをくまなく調べたところ、必要な機能を備えたユーティリティ クラスが見つかりました。
/* * Copyright (c) 2010 Google Inc. * * Apache ライセンス、バージョン 2.0 (「ライセンス」) に基づいてライセンス供与されています。* ライセンスに準拠する場合を除いて、このファイルを使用することはできません。* * http://www.apache.org/licenses/LICENSE-2.0 * * でライセンスのコピーを取得できます * * 適用される法律で義務付けられていないか、または書面で合意されていない限り、ライセンスに基づいて配布されるソフトウェア * は "明示または黙示の保証または条件なしで、「現状有姿」で提供されます。* ライセンスの下で許可と制限を管理する特定の言語については、ライセンスを参照してください。*/
package com.voice.mailer.server;
import java.io.IOException;
import java.util.HashSet;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.appengine.auth.oauth2.AppEngineCredentialStore;
import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.common.base.Preconditions;
import com.google.gdata.client.docs.DocsService;
/**
* Utility class for JDO persistence, OAuth flow helpers, and others.
*
* @author Yaniv Inbar
*/
class Utils {
/** Global instance of the HTTP transport. */
static final HttpTransport HTTP_TRANSPORT = new UrlFetchTransport();
static Credential credential = null;
/** Global instance of the JSON factory. */
static final JsonFactory JSON_FACTORY = new JacksonFactory();
private static GoogleClientSecrets clientSecrets = null;
private static final Logger log = Logger.getLogger(Utils.class.getName());
static GoogleClientSecrets getClientCredential() throws IOException {
if (clientSecrets == null) {
clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
Utils.class.getResourceAsStream("/client_secrets.json"));
Preconditions
.checkArgument(
!clientSecrets.getDetails().getClientId()
.startsWith("Enter ")
&& !clientSecrets.getDetails()
.getClientSecret()
.startsWith("Enter "),
"Download client_secrets.json file from https://code.google.com/apis/console/?api=calendar "
+ "into calendar-appengine-sample/src/main/resources/client_secrets.json");
}
return clientSecrets;
}
static String getRedirectUri(HttpServletRequest req) {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
static GoogleAuthorizationCodeFlow newFlow() throws IOException {
HashSet<String> s = new HashSet<String>();
s.add(DriveScopes.DRIVE_FILE);
s.add("https://docs.google.com/feeds/");
s.add("https://docs.googleusercontent.com/");
s.add("https://spreadsheets.google.com/feeds/");
s.add("http://www.google.com/accounts/AuthSubRequest");
s.add("http://schemas.google.com/acl/2007");
s.add("https://www.googleapis.com/auth/userinfo");
return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
JSON_FACTORY, getClientCredential(), s)
.setCredentialStore(new AppEngineCredentialStore())
.setAccessType("offline").build();
// http(s)://docs.google.com/feeds/
}
static Drive loadDriveClient() throws IOException {
if (credential == null) {
String userId = UserServiceFactory.getUserService()
.getCurrentUser().getUserId();
credential = newFlow().loadCredential(userId);
}
return new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.build();
}
static DocsService loadDocsService(String applicationName) {
if (credential == null) {
String userId = UserServiceFactory.getUserService()
.getCurrentUser().getUserId();
try {
credential = newFlow().loadCredential(userId);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
DocsService service = new DocsService(applicationName);
log.info("token: " + credential.getAccessToken());
service.setOAuth2Credentials(credential);
// service.setHeader("Authorization",
// "Bearer " + credential.getAccessToken());
return service;
}
/**
* Returns an {@link IOException} (but not a subclass) in order to work
* around restrictive GWT serialization policy.
*/
static IOException wrappedIOException(IOException e) {
if (e.getClass() == IOException.class) {
return e;
}
return new IOException(e.getMessage());
}
private Utils() {
}
}
これで、このクラスは Drive SDK の部分をうまく処理し、loadDriveClient メソッドを呼び出すことができるようになり、ロックンロールの準備が整ったクラスを取得できました。しかし、loadDocsService を呼び出してメソッドを呼び出すと、次のエラーが発生し続けます。
10, 2013 5:36:07 PM com.google.appengine.repackaged.org.apache.http.impl.client.DefaultRequestDirector handleResponse
WARNING: Authentication error: Unable to respond to any of these challenges: {authsub=WWW-Authenticate: AuthSub realm="http://www.google.com/accounts/AuthSubRequest"}
com.google.gdata.client.GoogleService$SessionExpiredException: OK
<HTML>
<HEAD>
<TITLE>Token invalid - AuthSub token has wrong scope</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Token invalid - AuthSub token has wrong scope</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
at com.google.gdata.client.http.GoogleGDataRequest.handleErrorResponse(GoogleGDataRequest.java:570)
at com.google.gdata.client.http.HttpGDataRequest.checkResponse(HttpGDataRequest.java:560)
at com.google.gdata.client.http.HttpGDataRequest.execute(HttpGDataRequest.java:538)
at com.google.gdata.client.http.GoogleGDataRequest.execute(GoogleGDataRequest.java:536)
at com.google.gdata.client.Service.getFeed(Service.java:1135)
at com.google.gdata.client.Service.getFeed(Service.java:998)
at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:652)
at com.google.gdata.client.Service.getFeed(Service.java:1017)
at com.voice.mailer.shared.DocumentList.getAclFeed(DocumentList.java:611)
at com.voice.mailer.server.NewFileProcessorImpl.processNewFiles(NewFileProcessorImpl.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:115)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:561)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:208)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:61)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:383)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Mar 10, 2013 5:36:21 PM com.google.appengine.api.datastore.dev.LocalDatastoreService$PersistDatastore persist
INFO: Time to persist datastore: 6 ms
考えられるすべてのことと、見つけられるすべての例を試しました。DocsService を正しく作成しない理由がわかりません。なぜこれがうまくいかないのか、何かアイデアを教えてもらえますか?
ありがとう