/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.host.controller;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST;
import static org.jboss.as.host.controller.logging.HostControllerLogger.ROOT_LOGGER;
import java.io.IOException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;
import org.jboss.as.controller.BlockingTimeout;
import org.jboss.as.controller.CurrentOperationIdHolder;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.ProxyController;
import org.jboss.as.controller.client.OperationResponse;
import org.jboss.as.controller.client.helpers.domain.ServerStatus;
import org.jboss.as.controller.extension.ExtensionRegistry;
import org.jboss.as.controller.remote.BlockingQueueOperationListener;
import org.jboss.as.controller.remote.TransactionalProtocolClient;
import org.jboss.as.controller.transform.TransformationTarget;
import org.jboss.as.controller.transform.TransformationTargetImpl;
import org.jboss.as.controller.transform.TransformerRegistry;
import org.jboss.as.domain.controller.DomainController;
import org.jboss.as.host.controller.logging.HostControllerLogger;
import org.jboss.as.process.ProcessController;
import org.jboss.as.process.ProcessControllerClient;
import org.jboss.as.process.ProcessInfo;
import org.jboss.as.process.ProcessMessageHandler;
import org.jboss.as.protocol.mgmt.ManagementChannelHandler;
import org.jboss.as.version.Version;
import org.jboss.dmr.ModelNode;
import org.jboss.remoting3.Channel;
import org.jboss.remoting3.CloseHandler;
import org.jboss.threads.AsyncFuture;
import org.wildfly.security.auth.callback.CredentialCallback;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.evidence.PasswordGuessEvidence;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.interfaces.DigestPassword;
import org.wildfly.security.password.spec.DigestPasswordAlgorithmSpec;
import org.wildfly.security.password.spec.EncryptablePasswordSpec;
/**
* Inventory of the managed servers.
*
* @author Emanuel Muckenhuber
* @author Kabir Khan
*/
public class ServerInventoryImpl implements ServerInventory {
/** The managed servers. */
private final ConcurrentMap<String, ManagedServer> servers = new ConcurrentHashMap<String, ManagedServer>();
private final HostControllerEnvironment environment;
private final ProcessControllerClient processControllerClient;
private final URI managementURI;
private final DomainController domainController;
private final ExtensionRegistry extensionRegistry;
private volatile boolean shutdown;
private volatile boolean connectionFinished;
//
private volatile CountDownLatch processInventoryLatch;
private volatile Map<String, ProcessInfo> processInfos;
private final Object shutdownCondition = new Object();
ServerInventoryImpl(final DomainController domainController, final HostControllerEnvironment environment, final URI managementURI,
final ProcessControllerClient processControllerClient, final ExtensionRegistry extensionRegistry) {
this.domainController = domainController;
this.environment = environment;
this.managementURI = managementURI;
this.processControllerClient = processControllerClient;
this.extensionRegistry = extensionRegistry;
}
@Override
public String getServerProcessName(String serverName) {
return ManagedServer.getServerProcessName(serverName);
}
@Override
public String getProcessServerName(String processName) {
return ManagedServer.getServerName(processName);
}
@Override
public synchronized Map<String, ProcessInfo> determineRunningProcesses() {
processInventoryLatch = new CountDownLatch(1);
try {
processControllerClient.requestProcessInventory();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (!processInventoryLatch.await(30, TimeUnit.SECONDS)){
throw HostControllerLogger.ROOT_LOGGER.couldNotGetServerInventory(30L, TimeUnit.SECONDS.toString().toLowerCase(Locale.US));
}
} catch (InterruptedException e) {
throw HostControllerLogger.ROOT_LOGGER.couldNotGetServerInventory(30L, TimeUnit.SECONDS.toString().toLowerCase(Locale.US));
}
return processInfos;
}
@Override
public Map<String, ProcessInfo> determineRunningProcesses(final boolean serversOnly) {
final Map<String, ProcessInfo> processInfos = determineRunningProcesses();
if (!serversOnly) {
return processInfos;
}
final Map<String, ProcessInfo> processes = new HashMap<String, ProcessInfo>();
for (Map.Entry<String, ProcessInfo> procEntry : processInfos.entrySet()) {
if (ManagedServer.isServerProcess(procEntry.getKey())) {
processes.put(procEntry.getKey(), procEntry.getValue());
}
}
return processes;
}
@Override
public ServerStatus determineServerStatus(final String serverName) {
final ManagedServer server = servers.get(serverName);
if(server == null) {
return ServerStatus.STOPPED;
}
return server.getState();
}
@Override
public ServerStatus startServer(final String serverName, final ModelNode domainModel) {
return startServer(serverName, domainModel, false, false);
}
@Override
public ServerStatus startServer(final String serverName, final ModelNode domainModel, final boolean blocking, boolean suspend) {
if(shutdown || connectionFinished) {
throw HostControllerLogger.ROOT_LOGGER.hostAlreadyShutdown();
}
ManagedServer server = servers.get(serverName);
if (server != null && server.getState() == ServerStatus.FAILED) {
//If the server failed stop it to
// refresh the parameters passed in to the new process
// read it in the process controller
HostControllerLogger.ROOT_LOGGER.failedToStartServer(null, serverName);
//The gracefulTimeout value does not seem to be used in this case, but initialise it to 1s just in case it gets added.
//In practise the server has already stopped so the time should be less than this
stopServer(serverName, 1000, true);
server = null;
}
if(server == null) {
// Create a new authKey
final byte[] authBytes = new byte[ProcessController.AUTH_BYTES_LENGTH];
new Random(new SecureRandom().nextLong()).nextBytes(authBytes);
String authKey = Base64.getEncoder().encodeToString(authBytes);
// Create the managed server
final ManagedServer newServer = createManagedServer(serverName, authKey);
server = servers.putIfAbsent(serverName, newServer);
if(server == null) {
server = newServer;
}
}
// Start the server
server.start(createBootFactory(serverName, domainModel, suspend));
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
if(blocking) {
// Block until the server started message
server.awaitState(ManagedServer.InternalState.SERVER_STARTED);
} else {
// Wait until the server opens the mgmt connection
server.awaitState(ManagedServer.InternalState.SERVER_STARTING);
}
return server.getState();
}
@Override
public ServerStatus restartServer(final String serverName, final int gracefulTimeout, final ModelNode domainModel) {
return restartServer(serverName, gracefulTimeout, domainModel, false, false);
}
@Override
public ServerStatus restartServer(final String serverName, final int gracefulTimeout, final ModelNode domainModel, final boolean blocking, final boolean suspend) {
stopServer(serverName, gracefulTimeout);
synchronized (shutdownCondition) {
for(;;) {
if(shutdown || connectionFinished) {
throw HostControllerLogger.ROOT_LOGGER.hostAlreadyShutdown();
}
if(! servers.containsKey(serverName)) {
break;
}
try {
shutdownCondition.wait();
} catch (InterruptedException e){
Thread.currentThread().interrupt();
break;
}
}
}
startServer(serverName, domainModel, blocking, suspend);
return determineServerStatus(serverName);
}
@Override
public ServerStatus stopServer(final String serverName, final int gracefulTimeout) {
return stopServer(serverName, gracefulTimeout, false);
}
@Override
public ServerStatus stopServer(final String serverName, final int gracefulTimeout, final boolean blocking) {
final ManagedServer server = servers.get(serverName);
if(server == null) {
return ServerStatus.STOPPED;
}
Integer currentOperationID = CurrentOperationIdHolder.getCurrentOperationID();
server.stop(currentOperationID == null ? -1 : currentOperationID, gracefulTimeout);
if(blocking) {
server.awaitState(ManagedServer.InternalState.STOPPED);
}
return server.getState();
}
@Override
public void reconnectServer(final String serverName, final ModelNode domainModel, final String authKey, final boolean running, final boolean stopping) {
if(shutdown || connectionFinished) {
throw HostControllerLogger.ROOT_LOGGER.hostAlreadyShutdown();
}
ManagedServer existing = servers.get(serverName);
if(existing != null) {
ROOT_LOGGER.existingServerWithState(serverName, existing.getState());
return;
}
final ManagedServer server = createManagedServer(serverName, authKey);
if ((existing = servers.putIfAbsent(serverName, server)) != null) {
ROOT_LOGGER.existingServerWithState(serverName, existing.getState());
return;
}
if(running) {
if(!stopping) {
server.reconnectServerProcess(createBootFactory(serverName, domainModel, false));
} else {
server.setServerProcessStopping();
}
} else {
server.removeServerProcess();
}
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public ServerStatus reloadServer(final String serverName, final boolean blocking, boolean suspend) {
if (shutdown || connectionFinished) {
throw HostControllerLogger.ROOT_LOGGER.hostAlreadyShutdown();
}
final ManagedServer server = servers.get(serverName);
if (server == null) {
return ServerStatus.STOPPED;
}
if (server.reload(CurrentOperationIdHolder.getCurrentOperationID(), suspend)) {
// Reload with current permit
if (blocking) {
server.awaitState(ManagedServer.InternalState.SERVER_STARTED);
} else {
server.awaitState(ManagedServer.InternalState.SERVER_STARTING);
}
}
return determineServerStatus(serverName);
}
@Override
public void destroyServer(String serverName) {
final ManagedServer server = servers.get(serverName);
if(server == null) {
return;
}
server.destroy();
}
@Override
public void killServer(String serverName) {
final ManagedServer server = servers.get(serverName);
if(server == null) {
return;
}
server.kill();
}
@Override
public void stopServers(final int gracefulTimeout) {
stopServers(gracefulTimeout, false);
}
@Override
public void stopServers(final int gracefulTimeout, final boolean blockUntilStopped) {
for(final ManagedServer server : servers.values()) {
Integer currentOperationID = CurrentOperationIdHolder.getCurrentOperationID();
server.stop(currentOperationID == null ? -1 : currentOperationID, gracefulTimeout);
}
if(blockUntilStopped) {
synchronized (shutdownCondition) {
for(;;) {
if(connectionFinished) {
break;
}
int count = 0;
for(final ManagedServer server : servers.values()) {
final ServerStatus state = server.getState();
switch (state) {
case DISABLED:
case FAILED:
case STOPPED:
break;
default:
count++;
}
}
if(count == 0) {
break;
}
try {
shutdownCondition.wait();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
@Override
// Hmm, maybe have startServer return some sort of Future, so the caller can decide to wait
public void awaitServersState(final Collection<String> serverNames, final boolean started) {
for (final String serverName : serverNames) {
final ManagedServer server = servers.get(serverName);
if(server == null) {
continue;
}
server.awaitState(started ? ManagedServer.InternalState.SERVER_STARTED : ManagedServer.InternalState.STOPPED);
}
}
@Override
public List<ModelNode> suspendServers(Set<String> serverNames, BlockingTimeout blockingTimeout) {
return suspendServers(serverNames, 0, blockingTimeout);
}
@Override
public List<ModelNode> resumeServers(Set<String> serverNames, BlockingTimeout blockingTimeout) {
List<ModelNode> errorResults = new ArrayList<>();
Map<String, OperationData> operationDataMap = new HashMap<>();
for (String serverName : serverNames) {
final ManagedServer server = servers.get(serverName);
if (server != null) {
try {
int blockingTimeoutValue = blockingTimeout.getProxyBlockingTimeout(server.getAddress(), server.getProxyController());
BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener = new BlockingQueueOperationListener<>();
AsyncFuture<OperationResponse> future = server.resume(listener);
operationDataMap.put(serverName, this.new OperationData(blockingTimeoutValue, future, listener));
} catch (IOException e) {
HostControllerLogger.ROOT_LOGGER.resumeExecutionFailed(e, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.resumeExecutionFailedMsg(serverName)
));
}
}
}
for (Map.Entry<String, OperationData> operationDataEntry : operationDataMap.entrySet()) {
final OperationData operationData = operationDataEntry.getValue();
final String serverName = operationDataEntry.getKey();
final int timeout = operationData.blockingTimeout;
final AsyncFuture<OperationResponse> future = operationData.future;
final BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener = operationData.listener;
try {
final TransactionalProtocolClient.PreparedOperation<?> prepared =
listener.retrievePreparedOperation(timeout, TimeUnit.MILLISECONDS);
if (prepared == null){
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingResumeResponse(timeout, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingResumeResponseMsg(timeout, serverName)
));
future.asyncCancel(true);
continue;
}
if (prepared.isFailed()) {
errorResults.add(appendServerNameToFailureResponse(serverName, prepared.getPreparedResult()));
continue;
}
prepared.commit();
prepared.getFinalResult().get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
HostControllerLogger.ROOT_LOGGER.interruptedAwaitingResumeResponse(e, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.interruptedAwaitingResumeResponseMsg(serverName)
));
future.asyncCancel(true);
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingResumeResponse(timeout, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingResumeResponseMsg(timeout, serverName)
));
future.asyncCancel(true);
} catch (ExecutionException e) {
HostControllerLogger.ROOT_LOGGER.resumeListenerFailed(e, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.resumeListenerFailedMsg(serverName)
));
future.asyncCancel(true);
}
}
return errorResults;
}
@Override
public List<ModelNode> suspendServers(Set<String> serverNames, int timeoutInSeconds, BlockingTimeout blockingTimeout) {
List<ModelNode> errorResults = new ArrayList<>();
Map<String, OperationData> operationDataMap = new HashMap<>();
for (String serverName : serverNames) {
final ManagedServer server = servers.get(serverName);
if (server != null) {
try {
int blockingTimeoutValue = blockingTimeout.getProxyBlockingTimeout(server.getAddress(), server.getProxyController());
BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener = new BlockingQueueOperationListener<>();
AsyncFuture<OperationResponse> future = server.suspend(timeoutInSeconds, listener);
operationDataMap.put(serverName, this.new OperationData(blockingTimeoutValue, future, listener));
} catch (IOException e) {
HostControllerLogger.ROOT_LOGGER.suspendExecutionFailed(e, serverName);
errorResults.add(
new ModelNode(HostControllerLogger.ROOT_LOGGER.suspendExecutionFailedMsg(serverName)
));
}
}
}
for (Map.Entry<String, OperationData> operationDataEntry : operationDataMap.entrySet()) {
final OperationData operationData = operationDataEntry.getValue();
final String serverName = operationDataEntry.getKey();
final int timeout = operationData.blockingTimeout;
final AsyncFuture<OperationResponse> future = operationData.future;
final BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener = operationData.listener;
try {
final TransactionalProtocolClient.PreparedOperation<?> prepared =
listener.retrievePreparedOperation(timeout, TimeUnit.MILLISECONDS);
if (prepared == null){
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingSuspendResponse(timeout, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingSuspendResponseMsg(timeout, serverName)
));
future.asyncCancel(true);
continue;
}
if (prepared.isFailed()) {
errorResults.add(appendServerNameToFailureResponse(serverName, prepared.getPreparedResult()));
continue;
}
prepared.commit();
prepared.getFinalResult().get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
HostControllerLogger.ROOT_LOGGER.interruptedAwaitingSuspendResponse(e, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.interruptedAwaitingSuspendResponseMsg(serverName)
));
future.asyncCancel(true);
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingSuspendResponse(timeout, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.timedOutAwaitingSuspendResponseMsg(timeout, serverName)
));
future.asyncCancel(true);
} catch (ExecutionException e) {
HostControllerLogger.ROOT_LOGGER.suspendListenerFailed(e, serverName);
errorResults.add( new ModelNode(
HostControllerLogger.ROOT_LOGGER.suspendListenerFailedMsg(serverName)
));
future.asyncCancel(true);
}
}
return errorResults;
}
void shutdown(final boolean shutdownServers, final int gracefulTimeout, final boolean blockUntilStopped) {
final boolean shutdown = this.shutdown;
this.shutdown = true;
if(! shutdown) {
if(connectionFinished) {
// In case the connection to the ProcessController is closed we won't be able to shutdown the servers from here
// nor can expect to receive any further notifications notifications.
return;
}
if(shutdownServers) {
// Shutdown the servers as well
stopServers(gracefulTimeout, blockUntilStopped);
}
}
}
@Override
public ProxyController serverCommunicationRegistered(final String serverProcessName, final ManagementChannelHandler channelAssociation) {
if(shutdown || connectionFinished) {
throw HostControllerLogger.ROOT_LOGGER.hostAlreadyShutdown();
}
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return null;
}
try {
final TransactionalProtocolClient client = server.channelRegistered(channelAssociation);
final Channel channel = channelAssociation.getChannel();
channel.addCloseHandler(new CloseHandler<Channel>() {
public void handleClose(final Channel closed, final IOException exception) {
final boolean shuttingDown = shutdown || connectionFinished;
// Unregister right away
if(server.callbackUnregistered(client, shuttingDown)) {
domainController.unregisterRunningServer(server.getServerName());
}
}
});
return server.getProxyController();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean serverReconnected(String serverProcessName, ManagementChannelHandler channelHandler) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
// Register the new communication channel
serverCommunicationRegistered(serverProcessName, channelHandler);
// Mark the server as started
serverStarted(serverProcessName);
// Register the server proxy at the domain controller
domainController.registerRunningServer(server.getProxyController());
// If the server requires a reload, means we are out of sync
return server.isRequiresReload() == false;
}
@Override
public void serverProcessStopped(final String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
// always un-register in case the process exits
domainController.unregisterRunningServer(server.getServerName());
server.processFinished();
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void connectionFinished() {
this.connectionFinished = true;
ROOT_LOGGER.debug("process controller connection closed.");
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void serverStarted(String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
server.serverStarted(null);
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void serverStartFailed(final String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
server.serverStartFailed();
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void serverProcessAdded(final String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
server.processAdded();
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void serverProcessStarted(final String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
server.processStarted();
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void serverUnstable(final String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
boolean change = true;
if (server != null) {
change = server.processUnstable();
}
if (change) {
// Pass the news on to the DC
domainController.reportServerInstability(serverName);
}
}
@Override
public void serverProcessRemoved(final String serverProcessName) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.remove(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
server.processRemoved();
synchronized (shutdownCondition) {
shutdownCondition.notifyAll();
}
}
@Override
public void operationFailed(final String serverProcessName, final ProcessMessageHandler.OperationType type) {
final String serverName = ManagedServer.getServerName(serverProcessName);
final ManagedServer server = servers.get(serverName);
if(server == null) {
ROOT_LOGGER.noServerAvailable(serverName);
return;
}
switch (type) {
case ADD:
server.transitionFailed(ManagedServer.InternalState.PROCESS_ADDING);
break;
case START:
server.transitionFailed(ManagedServer.InternalState.PROCESS_STARTING);
break;
case STOP:
server.transitionFailed(ManagedServer.InternalState.PROCESS_STOPPING);
break;
case SEND_STDIN:
case RECONNECT:
server.transitionFailed(ManagedServer.InternalState.SERVER_STARTING);
break;
case REMOVE:
server.transitionFailed(ManagedServer.InternalState.PROCESS_REMOVING);
break;
}
}
@Override
public void processInventory(final Map<String, ProcessInfo> processInfos) {
this.processInfos = processInfos;
if (processInventoryLatch != null){
processInventoryLatch.countDown();
}
}
private ManagedServer createManagedServer(final String serverName, final String authKey) {
final String hostControllerName = domainController.getLocalHostInfo().getLocalHostName();
// final ManagedServerBootConfiguration configuration = combiner.createConfiguration();
final Map<PathAddress, ModelVersion> subsystems = TransformerRegistry.resolveVersions(extensionRegistry);
final ModelVersion modelVersion = ModelVersion.create(Version.MANAGEMENT_MAJOR_VERSION, Version.MANAGEMENT_MINOR_VERSION, Version.MANAGEMENT_MICRO_VERSION);
//We don't need any transformation between host and server
final TransformationTarget target = TransformationTargetImpl.create(hostControllerName, extensionRegistry.getTransformerRegistry(),
modelVersion, subsystems, TransformationTarget.TransformationTargetType.SERVER);
return new ManagedServer(hostControllerName, serverName, authKey, processControllerClient, managementURI, target);
}
private ManagedServerBootCmdFactory createBootFactory(final String serverName, final ModelNode domainModel, boolean suspend) {
final String hostControllerName = domainController.getLocalHostInfo().getLocalHostName();
final ModelNode hostModel = domainModel.require(HOST).require(hostControllerName);
return new ManagedServerBootCmdFactory(serverName, domainModel, hostModel, environment, domainController.getExpressionResolver(), suspend);
}
@Override
public CallbackHandler getServerCallbackHandler() {
return new CallbackHandler() {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
List<Callback> toRespondTo = new LinkedList<Callback>();
String userName = null;
String realm = null;
ManagedServer server = null;
// A single pass may be sufficient but by using a two pass approach the Callbackhandler will not
// fail if an unexpected order is encountered.
// First Pass - is to double check no unsupported callbacks and to retrieve
// information from the callbacks passing in information.
for (Callback current : callbacks) {
if (current instanceof AuthorizeCallback) {
toRespondTo.add(current);
} else if (current instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) current;
userName = nameCallback.getDefaultName();
if (userName.startsWith("=")) {
server = servers.get(userName.substring(1));
}
} else if (current instanceof PasswordCallback) {
toRespondTo.add(current);
} else if (current instanceof EvidenceVerifyCallback) {
toRespondTo.add(current);
} else if (current instanceof CredentialCallback) {
toRespondTo.add(current);
} else if (current instanceof RealmCallback) {
realm = ((RealmCallback)current).getDefaultText();
} else {
throw new UnsupportedCallbackException(current);
}
}
/*
* At the moment this is a special CallbackHandler where we know the setting of a password will be double checked
* before going back to the base realm.
*/
if (server == null) {
return;
}
// Second Pass - Now iterate the Callback(s) requiring a response.
for (Callback current : toRespondTo) {
if (current instanceof AuthorizeCallback) {
AuthorizeCallback authorizeCallback = (AuthorizeCallback) current;
// Don't support impersonating another identity
authorizeCallback.setAuthorized(authorizeCallback.getAuthenticationID().equals(authorizeCallback.getAuthorizationID()));
} else if (current instanceof PasswordCallback) {
((PasswordCallback) current).setPassword(server.getAuthKey().toCharArray());
} else if (current instanceof EvidenceVerifyCallback) {
EvidenceVerifyCallback vpc = (EvidenceVerifyCallback) current;
vpc.setVerified(server.getAuthKey().equals(vpc.applyToEvidence(PasswordGuessEvidence.class, e -> new String(e.getGuess()))));
} else if (current instanceof CredentialCallback) {
CredentialCallback dhc = (CredentialCallback) current;
try {
if (realm == null) {
throw HostControllerLogger.ROOT_LOGGER.insufficientInformationToGenerateHash();
}
final PasswordFactory instance = PasswordFactory.getInstance(DigestPassword.ALGORITHM_DIGEST_MD5);
final Password password = instance.generatePassword(new EncryptablePasswordSpec(server.getAuthKey().toCharArray(), new DigestPasswordAlgorithmSpec(userName, realm)));
dhc.setCredential(new PasswordCredential(password));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw HostControllerLogger.ROOT_LOGGER.unableToGenerateHash(e);
}
}
}
}
};
}
private ModelNode appendServerNameToFailureResponse(String serverName, ModelNode failureResponse) {
String currentDescription = failureResponse.get(FAILURE_DESCRIPTION).asString();
return new ModelNode(String.format("%s server: %s", currentDescription, serverName));
}
private class OperationData {
int blockingTimeout;
AsyncFuture<OperationResponse> future;
BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener;
public OperationData(int blockingTimeout, AsyncFuture<OperationResponse> future, BlockingQueueOperationListener<TransactionalProtocolClient.Operation> listener) {
this.blockingTimeout = blockingTimeout;
this.future = future;
this.listener = listener;
}
}
}