Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHE-2369: add a mechanism which checks state of the ws agent #2530

Merged
merged 1 commit into from
Oct 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ protected void configure() {
new org.eclipse.che.api.machine.server.model.impl.ServerConfImpl(Constants.WSAGENT_DEBUG_REFERENCE, "4403/tcp", "http",
null));

bind(org.eclipse.che.api.agent.server.WsAgentHealthChecker.class)
.to(org.eclipse.che.api.agent.server.WsAgentHealthCheckerImpl.class);

bind(org.eclipse.che.api.machine.server.recipe.RecipeLoader.class);
Multibinder.newSetBinder(binder(), String.class, Names.named("predefined.recipe.path"))
.addBinding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;

import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper;
import org.eclipse.che.api.workspace.shared.dto.WsAgentHealthStateDto;
import org.eclipse.che.ide.api.dialogs.DialogFactory;
import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent;
import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient;
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.RestServiceInfo;
import org.eclipse.che.ide.rest.StringUnmarshaller;
import org.eclipse.che.ide.ui.loaders.LoaderPresenter;
import org.eclipse.che.ide.util.loging.Log;
import org.eclipse.che.ide.websocket.MachineMessageBus;
import org.eclipse.che.ide.websocket.MessageBus;
import org.eclipse.che.ide.websocket.MessageBusProvider;
import org.eclipse.che.ide.websocket.events.ConnectionClosedHandler;
Expand All @@ -41,42 +45,57 @@
import java.util.List;

import static com.google.common.collect.Lists.newArrayList;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING;
import static org.eclipse.che.ide.api.machine.WsAgentState.STARTED;
import static org.eclipse.che.ide.api.machine.WsAgentState.STOPPED;

/**
* Controls workspace agent's state, defines actions to be perform on different events related to websocket
* connection (close, open, error, etc.), checks http/websocket connection to control it's state. Currently
* there are only two states that a workspace agent can be at: {@link WsAgentState#STARTED} or
* {@link WsAgentState#STOPPED}.
*
* @author Roman Nikitenko
* @author Valeriy Svydenko
*/
@Singleton
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No single word about purpose of this class, the same for public methods.
Please add java docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

public class WsAgentStateController implements ConnectionOpenedHandler, ConnectionClosedHandler, ConnectionErrorHandler {

private final EventBus eventBus;
private final MessageBusProvider messageBusProvider;
private final AsyncRequestFactory asyncRequestFactory;
private DevMachine devMachine;
private final LoaderPresenter loader;

private final EventBus eventBus;
private final MessageBusProvider messageBusProvider;
private final DialogFactory dialogFactory;
private final AsyncRequestFactory asyncRequestFactory;
private final WorkspaceServiceClient workspaceServiceClient;
private final LoaderPresenter loader;
//not used now added it for future if it we will have possibility check that service available for client call
private final List<RestServiceInfo> availableServices;

private MessageBus messageBus;
private WsAgentState state;
private final List<RestServiceInfo> availableServices;
private DevMachine devMachine;
private MessageBus messageBus;
private WsAgentState state;
private List<AsyncCallback<MessageBus>> messageBusCallbacks = newArrayList();
private List<AsyncCallback<DevMachine>> devMachineCallbacks = newArrayList();

@Inject
public WsAgentStateController(EventBus eventBus,
public WsAgentStateController(WorkspaceServiceClient workspaceServiceClient,
EventBus eventBus,
LoaderPresenter loader,
MessageBusProvider messageBusProvider,
AsyncRequestFactory asyncRequestFactory,
LoaderPresenter loader) {
DialogFactory dialogFactory) {
this.workspaceServiceClient = workspaceServiceClient;
this.loader = loader;
this.eventBus = eventBus;
this.messageBusProvider = messageBusProvider;
this.asyncRequestFactory = asyncRequestFactory;
this.dialogFactory = dialogFactory;
this.availableServices = new ArrayList<>();
this.loader = loader;
}

/**
* Initialize state of the agent.
*
* @param devMachine
* development machine instance
*/
public void initialize(DevMachine devMachine) {
this.devMachine = devMachine;
this.state = STOPPED;
Expand All @@ -86,17 +105,14 @@ public void initialize(DevMachine devMachine) {

@Override
public void onClose(WebSocketClosedEvent event) {
Log.info(getClass(), "Test WS connection closed with code " + event.getCode() + " reason: " + event.getReason());
if (state.equals(STARTED)) {
state = STOPPED;
eventBus.fireEvent(WsAgentStateEvent.createWsAgentStoppedEvent());
if (STARTED.equals(state)) {
checkWsAgentHealth();
}
}

@Override
public void onError() {
Log.info(getClass(), "Test WS connection error");
if (state.equals(STARTED)) {
if (STARTED.equals(state)) {
state = STOPPED;
eventBus.fireEvent(WsAgentStateEvent.createWsAgentStoppedEvent());
}
Expand All @@ -117,27 +133,12 @@ public void run() {
}.scheduleRepeating(300);
}

private void started() {
state = STARTED;
loader.setSuccess(LoaderPresenter.Phase.STARTING_WORKSPACE_AGENT);

for (AsyncCallback<MessageBus> callback : messageBusCallbacks) {
callback.onSuccess(messageBus);
}
messageBusCallbacks.clear();

for (AsyncCallback<DevMachine> callback : devMachineCallbacks) {
callback.onSuccess(devMachine);
}
devMachineCallbacks.clear();

eventBus.fireEvent(WsAgentStateEvent.createWsAgentStartedEvent());
}

/** Returns state of the ws agent */
public WsAgentState getState() {
return state;
}

/** Returns instance of {@link MachineMessageBus}. */
public Promise<MessageBus> getMessageBus() {
return AsyncPromiseHelper.createFromAsyncRequest(new AsyncPromiseHelper.RequestCall<MessageBus>() {
@Override
Expand All @@ -151,63 +152,74 @@ public void makeCall(AsyncCallback<MessageBus> callback) {
});
}

public Promise<DevMachine> getDevMachine() {
return AsyncPromiseHelper.createFromAsyncRequest(new AsyncPromiseHelper.RequestCall<DevMachine>() {
/**
* Goto checking HTTP connection via getting all registered REST Services
*/
private void checkHttpConnection() {
//here we add trailing slash because {@link org.eclipse.che.api.core.rest.ApiInfoService} mapped in this way
String url = devMachine.getWsAgentBaseUrl() + '/';
asyncRequestFactory.createGetRequest(url).send(new StringUnmarshaller()).then(new Operation<String>() {
@Override
public void makeCall(AsyncCallback<DevMachine> callback) {
if (messageBus != null) {
callback.onSuccess(devMachine);
} else {
WsAgentStateController.this.devMachineCallbacks.add(callback);
public void apply(String result) throws OperationException {
try {
JSONObject object = JSONParser.parseStrict(result).isObject();
if (object.containsKey("rootResources")) {
JSONArray rootResources = object.get("rootResources").isArray();
for (int i = 0; i < rootResources.size(); i++) {
JSONObject rootResource = rootResources.get(i).isObject();
String regex = rootResource.get("regex").isString().stringValue();
String fqn = rootResource.get("fqn").isString().stringValue();
String path = rootResource.get("path").isString().stringValue();
availableServices.add(new RestServiceInfo(fqn, regex, path));
}
}
} catch (Exception exception) {
Log.warn(getClass(), "Parse root resources failed.");
}

checkWsConnection();
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
checkWsAgentHealth();
}
});
}

/**
* Goto checking HTTP connection via getting all registered REST Services
*/
private void checkHttpConnection() {
String url = devMachine.getWsAgentBaseUrl() + '/'; //here we add trailing slash because
// {@link org.eclipse.che.api.core.rest.ApiInfoService} mapped in this way
asyncRequestFactory.createGetRequest(url)
.send(new StringUnmarshaller())
.then(new Operation<String>() {
@Override
public void apply(String result) throws OperationException {
JSONObject object = null;
try {
object = JSONParser.parseStrict(result).isObject();
} catch (Exception exception) {
Log.warn(getClass(), "Parse root resources failed.");
}

if (object != null && object.containsKey("rootResources")) {
JSONArray rootResources = object.get("rootResources").isArray();
for (int i = 0; i < rootResources.size(); i++) {
JSONObject rootResource = rootResources.get(i).isObject();
String regex = rootResource.get("regex").isString().stringValue();
String fqn = rootResource.get("fqn").isString().stringValue();
String path = rootResource.get("path").isString().stringValue();
availableServices.add(new RestServiceInfo(fqn, regex, path));
}
}

checkWsConnection();
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
Log.error(getClass(), arg.getMessage());
new Timer() {
@Override
public void run() {
checkHttpConnection();
}
}.schedule(1000);
}
});
private void started() {
state = STARTED;
loader.setSuccess(LoaderPresenter.Phase.STARTING_WORKSPACE_AGENT);

for (AsyncCallback<MessageBus> callback : messageBusCallbacks) {
callback.onSuccess(messageBus);
}
messageBusCallbacks.clear();

for (AsyncCallback<DevMachine> callback : devMachineCallbacks) {
callback.onSuccess(devMachine);
}
devMachineCallbacks.clear();

eventBus.fireEvent(WsAgentStateEvent.createWsAgentStartedEvent());
}

private void checkStateOfWsAgent(WsAgentHealthStateDto agentHealthStateDto) {
final int statusCode = agentHealthStateDto.getCode();

String infoWindowTitle = "Workspace Agent";

if (statusCode == 200) {
dialogFactory.createMessageDialog(infoWindowTitle,
"Your workspace is not responding. To fix the problem, verify you have a good " +
"network connection and restart the workspace.",
null).show();
} else {
dialogFactory.createMessageDialog(infoWindowTitle,
"Your workspace has stopped responding. To fix the problem, " +
"restart the workspace in the dashboard.",
null).show();
}
}

/**
Expand All @@ -223,4 +235,20 @@ private void checkWsConnection() {
messageBus.addOnOpenHandler(this);
}

private void checkWsAgentHealth() {
workspaceServiceClient.getWsAgentState(devMachine.getWorkspace()).then(new Operation<WsAgentHealthStateDto>() {
@Override
public void apply(WsAgentHealthStateDto arg) throws OperationException {
if (RUNNING.equals(arg.getWorkspaceStatus())) {
checkStateOfWsAgent(arg);
}
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
Log.error(getClass(), arg.getMessage());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it handle internal server error in appropriate way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.workspace.shared.dto.EnvironmentDto;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.WsAgentHealthStateDto;

import java.util.List;

Expand Down Expand Up @@ -283,4 +284,14 @@ public interface WorkspaceServiceClient {
*/
Promise<Void> createSnapshot(String workspaceId);

/**
* Gets state of the workspace agent.
*
* @param workspaceId
* workspace ID
* @return a promise that will resolve when the snapshot has been created, or rejects with an error
* @see WorkspaceService#checkAgentHealth(String)
*/
Promise<WsAgentHealthStateDto> getWsAgentState(String workspaceId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.WsAgentHealthStateDto;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
Expand Down Expand Up @@ -381,4 +382,11 @@ public void makeCall(AsyncCallback<Void> callback) {
}
});
}

@Override
public Promise<WsAgentHealthStateDto> getWsAgentState(String workspaceId) {
return asyncRequestFactory.createGetRequest(baseHttpUrl + '/' + workspaceId + "/check")
.header(ACCEPT, APPLICATION_JSON)
.send(dtoUnmarshallerFactory.newUnmarshaller(WsAgentHealthStateDto.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;

import org.eclipse.che.ide.api.project.ProjectTypeServiceClient;
import org.eclipse.che.api.project.shared.dto.ProjectTypeDto;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.component.WsAgentComponent;
import org.eclipse.che.ide.api.project.ProjectTypeServiceClient;
import org.eclipse.che.ide.api.project.type.ProjectTypeRegistry;

import java.util.List;
Expand Down
Loading