// Copyright 2014 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
package com.google.appengine.tools.pipeline;
import static com.google.appengine.tools.pipeline.Job.immediate;
import static com.google.appengine.tools.pipeline.Job.waitFor;
import com.google.appengine.api.taskqueue.DeferredTask;
import com.google.appengine.api.taskqueue.DeferredTaskContext;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.RetryOptions;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
/**
* A collection of common jobs and utilities.
*
* @author ozarov@google.com (Arie Ozarov)
*/
public class Jobs {
private Jobs() {
// A utility class
}
/**
* An helper job to transform a {@link Value} result.
* @param <F> input value.
* @param <T> transformed value.
*/
public static class Transform<F, T> extends Job1<T, F> {
private static final long serialVersionUID = 1280795955105207728L;
private Function<F, T> function;
public Transform(Function<F, T> function) {
Preconditions.checkArgument(function instanceof Serializable, "Function not serializable");
this.function = function;
}
@Override
public Value<T> run(F from) throws Exception {
return immediate(function.apply(from));
}
}
private static class WaitForAllJob<T> extends Job1<T, T> {
private static final long serialVersionUID = 3151677893523195265L;
@Override
public Value<T> run(T value) {
return immediate(value);
}
}
public static JobSetting.WaitForSetting[] createWaitForSettingArray(Value<?>... values) {
JobSetting.WaitForSetting[] settings = new JobSetting.WaitForSetting[values.length];
int i = 0;
for (Value<?> value : values) {
settings[i++] = waitFor(value);
}
return settings;
}
public static <T> Value<T> waitForAll(Job<?> caller, Value<T> value, Value<?>... values) {
return caller.futureCall(new WaitForAllJob<T>(), value, createWaitForSettingArray(values));
}
public static <T> Value<T> waitForAll(Job<?> caller, T value, Value<?>... values) {
return caller.futureCall(new WaitForAllJob<T>(), immediate(value),
createWaitForSettingArray(values));
}
public static <T> Value<T> waitForAllAndDelete(
Job<?> caller, Value<T> value, Value<?>... values) {
return caller.futureCall(
new DeletePipelineJob<T>(caller.getPipelineKey().getName()),
value, createWaitForSettingArray(values));
}
public static <T> Value<T> waitForAllAndDelete(Job<?> caller, T value, Value<?>... values) {
return caller.futureCall(new DeletePipelineJob<T>(caller.getPipelineKey().getName()),
immediate(value), createWaitForSettingArray(values));
}
private static class DeletePipelineJob<T> extends Job1<T, T> {
private static final long serialVersionUID = -5440838671291502355L;
private static final Logger log = Logger.getLogger(DeletePipelineJob.class.getName());
private final String key;
DeletePipelineJob(String rootJobKey) {
this.key = rootJobKey;
}
@Override
public Value<T> run(T value) {
DeferredTask deleteRecordsTask = new DeferredTask() {
private static final long serialVersionUID = -7510918963650055768L;
@Override
public void run() {
PipelineService service = PipelineServiceFactory.newPipelineService();
try {
service.deletePipelineRecords(key);
log.info("Deleted pipeline: " + key);
} catch (IllegalStateException e) {
log.info("Failed to delete pipeline: " + key);
HttpServletRequest request = DeferredTaskContext.getCurrentRequest();
if (request != null) {
int attempts = request.getIntHeader("X-AppEngine-TaskExecutionCount");
if (attempts <= 5) {
log.info("Request to retry deferred task #" + attempts);
DeferredTaskContext.markForRetry();
return;
}
}
try {
service.deletePipelineRecords(key, true, false);
log.info("Force deleted pipeline: " + key);
} catch (Exception ex) {
log.log(Level.WARNING, "Failed to force delete pipeline: " + key, ex);
}
} catch (NoSuchObjectException e) {
// Already done
}
}
};
String queueName = Optional.fromNullable(getOnQueue()).or("default");
Queue queue = QueueFactory.getQueue(queueName);
queue.add(TaskOptions.Builder.withPayload(deleteRecordsTask).countdownMillis(10000)
.retryOptions(RetryOptions.Builder.withMinBackoffSeconds(2).maxBackoffSeconds(20)));
return immediate(value);
}
}
}