App Engine Java SDK version 1.7.0
[gae.git] / java / src / main / com / google / appengine / tools / admin / AppAdminImpl.java
blob172e33a32dcb7ba67c145644b77701a3c8834b61
1 // Copyright 2008 Google Inc. All Rights Reserved.
3 package com.google.appengine.tools.admin;
5 import com.google.appengine.tools.admin.AppAdminFactory.ApplicationProcessingOptions;
6 import com.google.appengine.tools.admin.AppAdminFactory.ConnectOptions;
7 import com.google.apphosting.utils.config.BackendsXml;
8 import com.google.apphosting.utils.config.BackendsYamlReader;
9 import com.google.apphosting.utils.config.CronXml;
11 import java.io.BufferedReader;
12 import java.io.File;
13 import java.io.FileOutputStream;
14 import java.io.FileReader;
15 import java.io.PrintWriter;
16 import java.io.Reader;
17 import java.io.StringWriter;
18 import java.lang.reflect.Constructor;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
23 /**
24 * Our implementation of the AppAdmin interface.
27 public class AppAdminImpl implements AppAdmin {
29 private ConnectOptions options;
30 private GenericApplication app;
31 private PrintWriter errorWriter;
32 private ApplicationProcessingOptions appOptions;
33 private final Class<? extends AppVersionUpload> appVersionUploadClass;
35 AppAdminImpl(ConnectOptions options, GenericApplication app, PrintWriter errorWriter,
36 ApplicationProcessingOptions appOptions,
37 Class<? extends AppVersionUpload> appVersionUploadClass) {
38 this.options = options;
39 this.app = app;
40 this.errorWriter = errorWriter;
41 this.appOptions = appOptions;
42 this.appVersionUploadClass = appVersionUploadClass;
45 protected ServerConnection getServerConnection(ConnectOptions options) {
46 return ServerConnectionFactory.getServerConnection(options);
49 public void update(UpdateListener listener) {
50 ServerConnection connection = getServerConnection(options);
51 doUpdate(connection, listener, null);
52 listener.onSuccess(new UpdateSuccessEvent(""));
55 public void updateBackend(String backendName, UpdateListener listener) {
56 ServerConnection connection = getServerConnection(options);
57 doUpdate(connection, listener, backendName);
58 listener.onSuccess(new UpdateSuccessEvent(""));
61 public void updateBackends(List<String> backendNames, UpdateListener listener) {
62 ServerConnection connection = getServerConnection(options);
63 for (String backendName : backendNames) {
64 doUpdate(connection, listener, backendName);
66 listener.onSuccess(new UpdateSuccessEvent(""));
69 public void updateAllBackends(UpdateListener listener) {
70 ServerConnection connection = getServerConnection(options);
71 if (app.getBackendsXml() != null) {
72 for (BackendsXml.Entry backend : app.getBackendsXml().getBackends()) {
73 doUpdate(connection, listener, backend.getName());
76 listener.onSuccess(new UpdateSuccessEvent(""));
79 public void rollback() {
80 rollbackBackend(null);
83 public void rollbackBackend(String backend) {
84 ServerConnection connection = getServerConnection(options);
85 try {
86 AppVersionUpload uploader = createAppVersionUpload(connection, app, backend);
87 uploader.forceRollback();
88 } catch (Throwable t) {
89 errorWriter.println("Unable to rollback:");
90 t.printStackTrace(errorWriter);
91 throw new AdminException("Unable to rollback app: " + t.getMessage(), t);
95 public void rollbackAllBackends() {
96 ServerConnection connection = getServerConnection(options);
97 if (app.getBackendsXml() != null) {
98 try {
99 for (BackendsXml.Entry backend : app.getBackendsXml().getBackends()) {
100 AppVersionUpload uploader = createAppVersionUpload(connection, app, backend.getName());
101 uploader.forceRollback();
103 } catch (Throwable t) {
104 errorWriter.println("Unable to rollback:");
105 t.printStackTrace(errorWriter);
106 throw new AdminException("Unable to rollback app: " + t.getMessage(), t);
111 public void setBackendState(String backendName, BackendsXml.State newState) {
112 String url;
113 switch (newState) {
114 case START:
115 url = "/api/backends/start";
116 break;
117 case STOP:
118 url = "/api/backends/stop";
119 break;
120 default:
121 throw new IllegalArgumentException("Cannot change to state: " + newState);
124 ServerConnection connection = getServerConnection(options);
125 try {
126 connection.post(url, "", "app_id", app.getAppId(), "backend", backendName);
127 } catch (Throwable t) {
128 errorWriter.println("Unable to change backend state:");
129 t.printStackTrace(errorWriter);
130 throw new AdminException("Unable to change backend state: " + t.getMessage(), t);
134 public List<BackendsXml.Entry> listBackends() {
135 ServerConnection connection = getServerConnection(options);
136 try {
137 String yaml = connection.post("/api/backends/list", "", "app_id", app.getAppId());
138 if (yaml.contains("No backends configured")) {
139 return Collections.<BackendsXml.Entry>emptyList();
140 } else {
141 BackendsXml xml = BackendsYamlReader.parse(yaml);
142 return xml.getBackends();
144 } catch (Throwable t) {
145 errorWriter.println("Unable to list backends:");
146 t.printStackTrace(errorWriter);
147 throw new AdminException("Unable to list backends: " + t.getMessage(), t);
151 public void deleteBackend(String backendName) {
152 ServerConnection connection = getServerConnection(options);
153 try {
154 connection.post("/api/backends/delete", "", "app_id", app.getAppId(), "backend", backendName);
155 } catch (Throwable t) {
156 errorWriter.println("Unable to delete backend:");
157 t.printStackTrace(errorWriter);
158 throw new AdminException("Unable to delete backend: " + t.getMessage(), t);
162 public void configureBackend(String backendName) {
163 ServerConnection connection = getServerConnection(options);
164 try {
165 connection.post("/api/backends/configure", app.getBackendsXml().toYaml(),
166 "app_id", app.getAppId(), "backend", backendName);
167 } catch (Throwable t) {
168 errorWriter.println("Unable to configure backend:");
169 t.printStackTrace(errorWriter);
170 throw new AdminException("Unable to configure backend: " + t.getMessage(), t);
174 public void updateIndexes() {
175 ServerConnection connection = getServerConnection(options);
176 try {
177 AppVersionUpload uploader = createAppVersionUpload(connection, app, null);
178 uploader.updateIndexes();
179 } catch (Throwable t) {
180 errorWriter.println("Unable to update indexes:");
181 t.printStackTrace(errorWriter);
182 throw new AdminException("Unable to update indexes for app: " + t.getMessage(), t);
186 public void updateCron() {
187 ServerConnection connection = getServerConnection(options);
188 try {
189 AppVersionUpload uploader = createAppVersionUpload(connection, app, null);
190 uploader.updateCron();
191 } catch (Throwable t) {
192 errorWriter.println("Unable to update cron entries:");
193 t.printStackTrace(errorWriter);
194 throw new AdminException("Unable to update cron entries for app: " + t.getMessage(), t);
198 public void updateQueues() {
199 ServerConnection connection = getServerConnection(options);
200 try {
201 AppVersionUpload uploader = createAppVersionUpload(connection, app, null);
202 uploader.updateQueue();
203 } catch (Throwable t) {
204 errorWriter.println("Unable to upload:");
205 t.printStackTrace(errorWriter);
206 throw new AdminException("Unable to update task queues for app: " + t.getMessage(), t);
210 public void updateDos() {
211 ServerConnection connection = getServerConnection(options);
212 try {
213 AppVersionUpload uploader = createAppVersionUpload(connection, app, null);
214 uploader.updateDos();
215 } catch (Throwable t) {
216 errorWriter.println("Unable to update DoS entries:");
217 t.printStackTrace(errorWriter);
218 throw new AdminException("Unable to update DoS entries for app: " + t.getMessage(), t);
222 @Override
223 public void setDefaultVersion() {
224 ServerConnection connection = getServerConnection(options);
225 try {
226 AppVersionUpload uploader = createAppVersionUpload(connection, app, null);
227 uploader.setDefaultVersion();
228 } catch (Throwable t) {
229 errorWriter.println("Unable to set default version:");
230 t.printStackTrace(errorWriter);
231 throw new AdminException("Unable to set default version for app: " + t.getMessage(), t);
235 public List<CronEntry> cronInfo() {
236 try {
237 List<CronEntry> result = new ArrayList<CronEntry>();
239 CronXml cron = app.getCronXml();
240 if (cron == null) {
241 return result;
243 for (CronXml.Entry entry : cron.getEntries()) {
244 result.add(new CronEntryImpl(entry.getUrl(), entry.getDescription(), entry.getSchedule(),
245 entry.getTimezone()));
247 return result;
248 } catch (Throwable t) {
249 errorWriter.println("Unable to display run times for cron entries:");
250 t.printStackTrace(errorWriter);
251 throw new AdminException("Unable to display run times for cron entries for app: "
252 + t.getMessage(), t);
256 @Override
257 public ResourceLimits getResourceLimits() {
258 ServerConnection connection = getServerConnection(options);
259 try {
260 return ResourceLimits.request(connection, app);
261 } catch (Throwable t) {
262 errorWriter.println("Unable to get resource limits:");
263 t.printStackTrace(errorWriter);
264 throw new AdminException("Unable to get resource limits: "
265 + t.getMessage(), t);
269 public void vacuumIndexes(ConfirmationCallback<IndexDeleter.DeleteIndexAction> callback,
270 UpdateListener listener) {
271 String appID = app.getAppId();
272 if (null == appID || appID.isEmpty()) {
273 String message = "This application does not have an ID.";
274 String detailMessage =
275 "The vacuum_indexes operation may not be performed for"
276 + " an application that does not have an ID.";
277 AdminException e = new AdminException(message);
278 listener.onFailure(new UpdateFailureEvent(e, message, detailMessage));
279 throw e;
281 ServerConnection connection = getServerConnection(options);
282 IndexDeleter deleter = new IndexDeleter(connection, app, callback, errorWriter, listener);
283 try {
284 deleter.deleteUnusedIndexes();
285 } catch (Exception e) {
286 String message = "Unable to perform vacuum_indexes";
287 listener.onFailure(new UpdateFailureEvent(e, message, e.getMessage()));
288 throw new AdminException(message, e);
292 public Reader requestLogs(int numDays, LogSeverity severity) {
293 ServerConnection connection = getServerConnection(options);
294 try {
295 File logFile = File.createTempFile(app.getAppId() + "-" + app.getVersion(), ".log");
296 logFile.deleteOnExit();
297 LogFetcher logFetcher = new LogFetcher(app, connection);
298 logFetcher.fetch(numDays, severity, new FileOutputStream(logFile));
299 return new BufferedReader(new FileReader(logFile));
300 } catch (Exception ex) {
301 throw new AdminException("Unable to retrieve the remote application logs:", ex);
306 * Deploy a new version of this application. If successful, this method will
307 * return without throwing an exception but will not call
308 * {@link UpdateListener#onSuccess(UpdateSuccessEvent)}. The caller is responsible for
309 * calling that method.
311 private void doUpdate(ServerConnection connection, UpdateListener listener, String backend) {
312 StringWriter detailsWriter = new StringWriter();
313 try {
314 AppVersionUpload uploader = createAppVersionUpload(connection, app,
315 backend);
316 ResourceLimits resourceLimits = ResourceLimits.request(connection, app);
317 app.resetProgress();
318 app.setListener(listener);
319 app.setDetailsWriter(new PrintWriter(detailsWriter, true));
320 app.createStagingDirectory(appOptions, resourceLimits);
321 uploader.doUpload(resourceLimits);
322 } catch (Throwable t) {
323 errorWriter.println("Unable to update:");
324 t.printStackTrace(errorWriter);
325 listener.onFailure(new UpdateFailureEvent(t, t.toString(), detailsWriter.toString()));
326 throw new AdminException("Unable to update app: " + t.getMessage(), t);
330 private AppVersionUpload createAppVersionUpload(ServerConnection connection,
331 GenericApplication app, String backend) throws Exception {
332 Constructor<? extends AppVersionUpload> constructor =
333 appVersionUploadClass.getConstructor(ServerConnection.class, GenericApplication.class,
334 String.class, Boolean.TYPE);
335 return constructor.newInstance(connection, app, backend, appOptions.isBatchModeSet());