/**
* Copyright 2015 Palantir Technologies, 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.palantir.giraffe.command;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;
import com.google.common.util.concurrent.MoreExecutors;
import com.palantir.giraffe.command.spi.ExecutionSystemProvider;
import com.palantir.giraffe.internal.LocalExecutionSystemProvider;
import com.palantir.giraffe.internal.SchemeProviderFinder;
/**
* Factory methods for execution systems.
* <p>
* The first invocation of any method in this class triggers loading of the
* default execution system provider, identified by the URI {@code exec:///}. If
* loading this provider fails for any reason, an unspecified error is thrown.
* <p>
* The first invocation of {@link ExecutionSystemProvider#installedProviders()}
* from either {@link #newExecutionSystem(URI, Map) newExecutionSystem} or
* {@link #getExecutionSystem(URI) getExecutionSystem} triggers loading of all
* installed providers. This loading process is described by
* {@link ExecutionSystemProvider}.
* <p>
* This class also defines a factory method that accepts a {@link ClassLoader},
* enabling loading of providers from non-standard locations. The class loader
* is only used if no suitable provider is found using the default methods.
*
* @author bkeyes
*/
public final class ExecutionSystems {
private static final class DefaultExecutionSystemHolder {
private static final ExecutionSystem defaultExecutionSystem = defaultExecutionSystem();
private static ExecutionSystem defaultExecutionSystem() {
// TODO(bkeyes): consider allowing clients to replace the default provider
ExecutionSystemProvider provider = new LocalExecutionSystemProvider();
return provider.getExecutionSystem(URI.create("exec:///"));
}
}
/**
* Returns the default execution system for the local machine.
*/
public static ExecutionSystem getDefault() {
return DefaultExecutionSystemHolder.defaultExecutionSystem;
}
/**
* Gets an existing execution system with the specified URI.
*
* @param uri the URI of the desired system
*
* @throws ExecutionSystemNotFoundException if there is no execution system
* with the given URI
*/
public static ExecutionSystem getExecutionSystem(URI uri) {
List<ExecutionSystemProvider> providers = ExecutionSystemProvider.installedProviders();
return new SchemeProviderFinder<>(ExecutionSystemProvider.class, providers)
.findOrThrow(uri.getScheme())
.getExecutionSystem(uri);
}
/**
* Creates a new execution system for the specified URI. The number of
* execution systems with a given URI that may open at the same time is
* implementation dependent.
*
* @param uri the URI of the desired system
* @param env a map of implementation-specific parameters required to create
* the system
*
* @return a new {@code ExecutionSystem}
*
* @throws ExecutionSystemAlreadyExistsException if an execution system with
* the specified URI already exists and the implementation forbids
* duplicate systems
* @throws IOException is an I/O error occurs while creating and opening the
* execution system
*/
public static ExecutionSystem newExecutionSystem(URI uri, Map<String, ?> env)
throws IOException {
return newExecutionSystem(uri, env, null);
}
/**
* Creates a new execution system for the specified URI. The number of
* execution systems with a given URI that may open at the same time is
* implementation dependent.
*
* @param uri the URI of the desired system
* @param env a map of implementation-specific parameters required to create
* the system
* @param loader the {@code ClassLoader} to search for providers if no
* matching one is found in the default class loader
*
* @return a new {@code ExecutionSystem}
*
* @throws ExecutionSystemAlreadyExistsException if an execution system with
* the specified URI already exists and the implementation forbids
* duplicate systems
* @throws IOException is an I/O error occurs while creating and opening the
* execution system
*/
public static ExecutionSystem newExecutionSystem(URI uri, Map<String, ?> env,
ClassLoader loader) throws IOException {
List<ExecutionSystemProvider> providers = ExecutionSystemProvider.installedProviders();
return new SchemeProviderFinder<>(ExecutionSystemProvider.class, providers)
.setFallbackLoader(loader)
.findOrThrow(uri.getScheme())
.newExecutionSystem(uri, env);
}
/**
* Closes any connections the ExecutionSystem uses to execute commands after the
* CommandFuture finishes, even if other commands are still executing over the connections.
* This does nothing when called on local command futures.
*
* @param commandFuture the command future corresponding to an asynchronous command
*/
public static void closeAfterCompletion(final ExecutionSystem es,
CommandFuture commandFuture) {
commandFuture.addListener(new Runnable() {
@Override
public void run() {
try {
es.close();
} catch (IOException e) {
//TODO(jchien): Log
}
}
}, MoreExecutors.sameThreadExecutor());
}
/**
* Blocks the registration of any new futures on the ExecutionSystemShutdownListener.
* Queues a shutdown of the ExecutionSystem, closing any connections used to execute commands.
* Any non-registered commands still executing will be terminated.
*/
public static void closeAfterCompletion(ExecutionSystem es,
CommandExitLatch listener) {
listener.startMonitoring(es);
}
private ExecutionSystems() {
throw new UnsupportedOperationException();
}
}