1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.tools
.remoteapi
;
5 import com
.google
.api
.client
.auth
.oauth2
.Credential
;
6 import com
.google
.api
.client
.googleapis
.auth
.oauth2
.GoogleCredential
;
7 import com
.google
.api
.client
.googleapis
.compute
.ComputeCredential
;
8 import com
.google
.api
.client
.googleapis
.javanet
.GoogleNetHttpTransport
;
9 import com
.google
.api
.client
.http
.HttpTransport
;
10 import com
.google
.api
.client
.json
.jackson
.JacksonFactory
;
11 import com
.google
.apphosting
.api
.ApiProxy
;
12 import com
.google
.common
.collect
.ImmutableList
;
15 import java
.io
.IOException
;
16 import java
.io
.InputStream
;
17 import java
.security
.GeneralSecurityException
;
18 import java
.security
.PrivateKey
;
19 import java
.util
.List
;
22 * A mutable object containing settings for installing the remote API.
24 * <p>Example for connecting to a development app server:</p>
27 * RemoteApiOptions options = new RemoteApiOptions()
28 * .server("localhost", 8888),
29 * .useDevelopmentServerCredential();
32 * <p>Example for connecting to a deployed app:</p>
35 * RemoteApiOptions options = new RemoteApiOptions()
36 * .server("myappid.appspot.com", 443),
37 * .useApplicationDefaultCredential();
41 * The options should be passed to {@link RemoteApiInstaller#install}.
45 public class RemoteApiOptions
{
47 private static final List
<String
> OAUTH_SCOPES
= ImmutableList
.of(
48 "https://www.googleapis.com/auth/appengine.apis",
49 "https://www.googleapis.com/auth/userinfo.email");
51 private static final String LOCAL_USER
= "test@example.com";
52 private static final String LOCAL_PASSWORD
= "";
54 private String hostname
;
56 private String userEmail
;
57 private String password
;
58 private String credentialsToReuse
;
59 private String remoteApiPath
= "/remote_api";
60 private int maxConcurrentRequests
= 5;
61 private int datastoreQueryFetchSize
= 500;
62 private int maxHttpResponseSize
= 33 * 1024 * 1024;
64 private Credential oauthCredential
;
65 private HttpTransport httpTransport
;
67 public RemoteApiOptions() {}
69 RemoteApiOptions(RemoteApiOptions original
) {
70 this.hostname
= original
.hostname
;
71 this.port
= original
.port
;
72 this.userEmail
= original
.userEmail
;
73 this.password
= original
.password
;
74 this.credentialsToReuse
= original
.credentialsToReuse
;
75 this.remoteApiPath
= original
.remoteApiPath
;
76 this.maxConcurrentRequests
= original
.maxConcurrentRequests
;
77 this.datastoreQueryFetchSize
= original
.datastoreQueryFetchSize
;
78 this.maxHttpResponseSize
= original
.maxHttpResponseSize
;
79 this.oauthCredential
= original
.oauthCredential
;
80 this.httpTransport
= original
.httpTransport
;
84 * Sets the host and port port where we will connect.
86 public RemoteApiOptions
server(String newHostname
, int newPort
) {
87 hostname
= newHostname
;
93 * Sets a username and password to be used for logging in via the
94 * ClientLogin API. Overrides any previously-provided credentials.
96 * @deprecated Use {@link #useApplicationDefaultCredential} or
97 * {@link useServiceAccountCredential} instead.
100 public RemoteApiOptions
credentials(String newUserEMail
, String newPassword
) {
101 userEmail
= newUserEMail
;
102 password
= newPassword
;
103 credentialsToReuse
= null;
104 oauthCredential
= null;
109 * Reuses credentials from another AppEngineClient. Credentials can only
110 * be reused from a client with the same hostname and user. Overrides any
111 * previously-provided credentials.
112 * @param newUserEmail the email address of the user we want to log in as.
113 * @param serializedCredentials a string returned by calling
114 * {@link AppEngineClient#serializeCredentials} on the previous client
116 public RemoteApiOptions
reuseCredentials(String newUserEmail
, String serializedCredentials
) {
117 userEmail
= newUserEmail
;
119 credentialsToReuse
= serializedCredentials
;
120 oauthCredential
= null;
125 * Use a Compute Engine credential for authentication. Overrides any
126 * previously-provided credentials.
128 * @return this {@code RemoteApiOptions} instance
129 * @deprecated Use {@link #useApplicationDefaultCredential}.
132 public RemoteApiOptions
useComputeEngineCredential() {
134 HttpTransport transport
= getOrCreateHttpTransportForOAuth();
135 ComputeCredential credential
= new ComputeCredential(transport
, new JacksonFactory());
136 credential
.refreshToken();
137 setOAuthCredential(credential
);
138 } catch (IOException
|GeneralSecurityException e
) {
139 throw new RuntimeException("Failed to acquire Google Compute Engine credential.", e
);
145 * Use a Google Application Default credential for authentication. Overrides any
146 * previously-provided credentials.
148 * @return this {@code RemoteApiOptions} instance.
149 * @see <a href="developers.google.com/identity/protocols/application-default-credentials">
150 * Application Default Credentials</a>
152 public RemoteApiOptions
useApplicationDefaultCredential() {
154 getOrCreateHttpTransportForOAuth();
155 GoogleCredential credential
= GoogleCredential
.getApplicationDefault();
156 credential
= credential
.createScoped(OAUTH_SCOPES
);
157 credential
.refreshToken();
158 setOAuthCredential(credential
);
159 } catch (IOException
|GeneralSecurityException e
) {
160 throw new RuntimeException("Failed to acquire Google Application Default credential.", e
);
166 * Use a service account credential. Overrides any previously-provided
169 * @param serviceAccountId service account ID (typically an e-mail address)
170 * @param p12PrivateKeyFile p12 file containing a private key to use with the service account
172 * @return this {@code RemoteApiOptions} instance
174 public RemoteApiOptions
useServiceAccountCredential(String serviceAccountId
,
175 String p12PrivateKeyFile
) {
177 Credential credential
= getCredentialBuilder(serviceAccountId
)
178 .setServiceAccountPrivateKeyFromP12File(new File(p12PrivateKeyFile
))
180 setOAuthCredential(credential
);
181 } catch (IOException
|GeneralSecurityException e
) {
182 throw new RuntimeException("Failed to build service account credential.", e
);
188 * Use a service account credential. Overrides any previously-provided
191 * @param serviceAccountId service account ID (typically an e-mail address)
192 * @param privateKey private key to use with the service account
194 * @return this {@code RemoteApiOptions} instance
196 public RemoteApiOptions
useServiceAccountCredential(String serviceAccountId
,
197 PrivateKey privateKey
) {
199 Credential credential
= getCredentialBuilder(serviceAccountId
)
200 .setServiceAccountPrivateKey(privateKey
)
202 setOAuthCredential(credential
);
203 } catch (IOException
|GeneralSecurityException e
) {
204 throw new RuntimeException("Failed to build service account credential.", e
);
210 * Use credentials appropriate for talking to the Development Server.
211 * Overrides any previously-provided credentials.
213 * @return this {@code RemoteApiOptions} instance
215 public RemoteApiOptions
useDevelopmentServerCredential() {
216 credentials(LOCAL_USER
, LOCAL_PASSWORD
);
220 private GoogleCredential
.Builder
getCredentialBuilder(
221 String serviceAccountId
) throws GeneralSecurityException
, IOException
{
222 HttpTransport transport
= getOrCreateHttpTransportForOAuth();
223 JacksonFactory jsonFactory
= new JacksonFactory();
224 return new GoogleCredential
.Builder()
225 .setTransport(transport
)
226 .setJsonFactory(jsonFactory
)
227 .setServiceAccountId(serviceAccountId
)
228 .setServiceAccountScopes(OAUTH_SCOPES
);
231 RemoteApiOptions
useGoogleCredentialStream(InputStream stream
) {
233 getOrCreateHttpTransportForOAuth();
234 GoogleCredential credential
= GoogleCredential
.fromStream(stream
);
235 credential
= credential
.createScoped(OAUTH_SCOPES
);
236 credential
.refreshToken();
237 setOAuthCredential(credential
);
238 } catch (IOException
|GeneralSecurityException e
) {
239 throw new RuntimeException("Failed to acquire Google credential.", e
);
244 RemoteApiOptions
oauthCredential(Credential oauthCredential
) {
245 setOAuthCredential(oauthCredential
);
249 private void setOAuthCredential(Credential oauthCredential
) {
252 credentialsToReuse
= null;
253 this.oauthCredential
= oauthCredential
;
256 RemoteApiOptions
httpTransport(HttpTransport httpTransport
) {
257 this.httpTransport
= httpTransport
;
262 * Sets the path used to access the remote API. If not set, the default
265 public RemoteApiOptions
remoteApiPath(String newPath
) {
266 remoteApiPath
= newPath
;
271 * This parameter controls the maximum number of async API requests that will be
272 * in flight at once. Each concurrent request will likely be handled by a separate
273 * <a href="http://cloud.google.com/appengine/docs/adminconsole/instances.html"
274 * >instance</a> of your App. Having more instances increases throughput but may
275 * result in errors due to exceeding quota. Defaults to 5.
277 public RemoteApiOptions
maxConcurrentRequests(int newValue
) {
278 maxConcurrentRequests
= newValue
;
283 * When executing a datastore query, this is the number of results to fetch
284 * per HTTP request. Increasing this value will reduce the number of round trips
285 * when running large queries, but too high a value can be wasteful when not
286 * all results are needed. Defaults to 500.
288 * <p>(This value can be overridden by the code using the datastore API.)</p>
290 public RemoteApiOptions
datastoreQueryFetchSize(int newValue
) {
291 datastoreQueryFetchSize
= newValue
;
296 * When making a remote call, this is the maximum size of the HTTP response.
297 * The default is 33M. Normally there's no reason to change this. This
298 * setting has no effect when running in an App Engine container.
300 public RemoteApiOptions
maxHttpResponseSize(int newValue
) {
301 maxHttpResponseSize
= newValue
;
305 public RemoteApiOptions
copy() {
306 return new RemoteApiOptions(this);
310 * Create an {@link HttpTransport} appropriate to this environment or return
311 * the one that's already been created. This method ensures that the
312 * determination of whether we're running in App Engine happens early
313 * (specifically, before the Remote API has been installed) and that said
314 * determination is remembered.
316 private HttpTransport
getOrCreateHttpTransportForOAuth()
317 throws IOException
, GeneralSecurityException
{
318 if (httpTransport
!= null) {
319 return httpTransport
;
322 if (ApiProxy
.getCurrentEnvironment() != null) {
323 throw new IllegalStateException(
324 "OAuth-based authorization not supported for clients running on App Engine");
327 httpTransport
= GoogleNetHttpTransport
.newTrustedTransport();
328 return httpTransport
;
331 HttpTransport
getHttpTransport() {
332 return httpTransport
;
335 public String
getHostname() {
339 public int getPort() {
343 public String
getUserEmail() {
347 public String
getPassword() {
351 public String
getCredentialsToReuse() {
352 return credentialsToReuse
;
355 Credential
getOAuthCredential() {
356 return oauthCredential
;
359 public String
getRemoteApiPath() {
360 return remoteApiPath
;
363 public int getMaxConcurrentRequests() {
364 return maxConcurrentRequests
;
367 public int getDatastoreQueryFetchSize() {
368 return datastoreQueryFetchSize
;
371 public int getMaxHttpResponseSize() {
372 return maxHttpResponseSize
;