/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate licenses this file * to you 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.jobs; import io.crate.exceptions.JobKilledException; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; public abstract class AbstractExecutionSubContext implements ExecutionSubContext { protected final Logger logger; protected final int id; private final AtomicBoolean firstClose = new AtomicBoolean(false); private final CompletionState completionState = new CompletionState(); private final CompletableFuture<CompletionState> future = new CompletableFuture<>(); protected AbstractExecutionSubContext(int id, Logger logger) { this.id = id; this.logger = logger; } public int id() { return id; } protected void innerStart() { } protected void innerPrepare() throws Exception { } @Override public final void prepare() throws Exception { try { innerPrepare(); } catch (Exception e) { cleanup(); throw e; } } @Override public final void start() { if (!firstClose.get()) { logger.trace("starting id={} ctx={}", id, this); try { innerStart(); } catch (Throwable t) { close(t); } } } protected void innerClose(@Nullable Throwable t) { } protected boolean close(@Nullable Throwable t) { if (firstClose.compareAndSet(false, true)) { logger.trace("closing id={} ctx={}", id, this); try { innerClose(t); } catch (Throwable t2) { if (t == null) { t = t2; } else { logger.warn("closing due to exception, but closing also throws exception", t2); } } finally { cleanup(); } completeFuture(t); return true; } return false; } private void completeFuture(@Nullable Throwable t) { if (t == null) { future.complete(completionState); } else { future.completeExceptionally(t); } } final public void close() { close(null); } protected void innerKill(@Nonnull Throwable t) { } @Override final public void kill(@Nullable Throwable t) { if (firstClose.compareAndSet(false, true)) { if (t == null) { t = new InterruptedException(JobKilledException.MESSAGE); } logger.trace("killing id={} ctx={} cause={}", id, this, t); try { innerKill(t); } catch (Throwable t2) { logger.warn("killing due to exception, but killing also throws exception", t2); } finally { cleanup(); } completeFuture(t); } } @Override public void cleanup() { } @Override public CompletableFuture<CompletionState> completionFuture() { return future; } protected void setBytesUsed(long bytesUsed) { completionState.bytesUsed(bytesUsed); } protected synchronized boolean isClosed() { return firstClose.get() || future.isDone(); } }