/* * JBoss, Home of Professional Open Source. * Copyright 2012, 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.server.mgmt.domain; import java.io.Closeable; import java.io.DataInput; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.ExecutorService; import org.jboss.as.controller.AbstractControllerService; import org.jboss.as.controller.ModelController; import org.jboss.as.controller.client.OperationAttachments; import org.jboss.as.controller.client.OperationMessageHandler; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.protocol.mgmt.AbstractManagementRequest; import org.jboss.as.protocol.mgmt.ActiveOperation; import org.jboss.as.protocol.mgmt.FlushableDataOutput; import org.jboss.as.protocol.mgmt.ManagementChannelHandler; import org.jboss.as.protocol.mgmt.ManagementRequestContext; import org.jboss.as.repository.RemoteFileRequestAndHandler; import org.jboss.as.server.logging.ServerLogger; import org.jboss.as.server.operations.ServerProcessStateHandler; import org.jboss.dmr.ModelNode; /** * Client used to interact with the local host controller. * * @author Emanuel Muckenhuber */ public class HostControllerClient implements AbstractControllerService.ControllerInstabilityListener, Closeable { private final String serverName; private final HostControllerConnection connection; private final ManagementChannelHandler channelHandler; private final RemoteFileRepositoryExecutorImpl repositoryExecutor; private volatile ModelController controller; private final boolean managementSubsystemEndpoint; private final ExecutorService executorService; private final Runnable unstableNotificationRunnable; HostControllerClient(final String serverName, final ManagementChannelHandler channelHandler, final HostControllerConnection connection, final boolean managementSubsystemEndpoint, final ExecutorService executorService) { this.serverName = serverName; this.connection = connection; this.channelHandler = channelHandler; this.repositoryExecutor = new RemoteFileRepositoryExecutorImpl(); this.managementSubsystemEndpoint = managementSubsystemEndpoint; this.executorService = executorService; // Create and cache the objects that will send any controller instability requests // in order to increase the potential that it will execute in low memory situations final ControllerInstabilityNotificationRequest request = new ControllerInstabilityNotificationRequest(); this.unstableNotificationRunnable = new Runnable() { @Override public void run() { try { channelHandler.executeRequest(request, null); } catch (Throwable t) { // not much we can do. Likely an OOME } } }; } /** * Get the server name. * * @return the server name */ public String getServerName() { return serverName; } /** * Resolve the boot updates and register at the local HC. * * @param controller the model controller * @param callback the completed callback * @throws Exception for any error */ void resolveBootUpdates(final ModelController controller, final ActiveOperation.CompletedCallback<ModelNode> callback) throws Exception { connection.openConnection(controller, callback); // Keep a reference to the the controller this.controller = controller; } public void reconnect(final URI uri, final String authKey, final boolean mgmtSubsystemEndpoint) throws IOException, URISyntaxException { // In case the server is out of sync after the reconnect, set reload required final boolean mgmtEndpointChanged = this.managementSubsystemEndpoint != mgmtSubsystemEndpoint; connection.asyncReconnect(uri, authKey, new HostControllerConnection.ReconnectCallback() { @Override public void reconnected(boolean inSync) { if (!inSync || mgmtEndpointChanged) { final ModelNode operation = new ModelNode(); operation.get(ModelDescriptionConstants.OP).set(ServerProcessStateHandler.REQUIRE_RELOAD_OPERATION); operation.get(ModelDescriptionConstants.OP_ADDR).setEmptyList(); controller.execute(operation, OperationMessageHandler.DISCARD, ModelController.OperationTransactionControl.COMMIT, OperationAttachments.EMPTY); } } }); } /** * Get the remote file repository. * * @return the remote file repository */ RemoteFileRepositoryExecutor getRemoteFileRepository() { return repositoryExecutor; } @Override public void close() throws IOException { if(connection != null) { connection.close(); } } /** * Sends the instability notification on to the managing HostController. * {@inheritDoc} */ @Override public void controllerUnstable() { try { executorService.submit(unstableNotificationRunnable); } catch (Throwable t) { // See if we can run it directly unstableNotificationRunnable.run(); } } private static class GetFileRequest extends AbstractManagementRequest<File, Void> { private final String hash; private final File localDeploymentFolder; private GetFileRequest(final String hash, final File localDeploymentFolder) { this.hash = hash; this.localDeploymentFolder = localDeploymentFolder; } @Override public byte getOperationType() { return DomainServerProtocol.GET_FILE_REQUEST; } @Override protected void sendRequest(ActiveOperation.ResultHandler<File> resultHandler, ManagementRequestContext<Void> context, FlushableDataOutput output) throws IOException { //The root id does not matter here ServerToHostRemoteFileRequestAndHandler.INSTANCE.sendRequest(output, (byte)0, hash); } @Override public void handleRequest(DataInput input, ActiveOperation.ResultHandler<File> resultHandler, ManagementRequestContext<Void> context) throws IOException { try { File first = new File(localDeploymentFolder, hash.substring(0,2)); File localPath = new File(first, hash.substring(2)); ServerToHostRemoteFileRequestAndHandler.INSTANCE.handleResponse(input, localPath, ServerLogger.ROOT_LOGGER, resultHandler, context); resultHandler.done(null); } catch (RemoteFileRequestAndHandler.CannotCreateLocalDirectoryException e) { resultHandler.failed(ServerLogger.ROOT_LOGGER.cannotCreateLocalDirectory(e.getDir())); } catch (RemoteFileRequestAndHandler.DidNotReadEntireFileException e) { resultHandler.failed(ServerLogger.ROOT_LOGGER.didNotReadEntireFile(e.getMissing())); } } } private class RemoteFileRepositoryExecutorImpl implements RemoteFileRepositoryExecutor { public File getFile(final String relativePath, final byte repoId, final File localDeploymentFolder) { try { return channelHandler.executeRequest(new GetFileRequest(relativePath, localDeploymentFolder), null).getResult().get(); } catch (Exception e) { throw ServerLogger.ROOT_LOGGER.failedToGetFileFromRemoteRepository(e); } } } private static class ControllerInstabilityNotificationRequest extends AbstractManagementRequest<Void, Void> { private ControllerInstabilityNotificationRequest() { } @Override public byte getOperationType() { return DomainServerProtocol.SERVER_INSTABILITY_REQUEST; } @Override protected void sendRequest(ActiveOperation.ResultHandler<Void> resultHandler, ManagementRequestContext<Void> context, FlushableDataOutput output) throws IOException { // nothing to add } @Override public void handleRequest(DataInput input, ActiveOperation.ResultHandler<Void> resultHandler, ManagementRequestContext<Void> context) throws IOException { resultHandler.done(null); } } }