diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiEndpointAccessibilityChecker.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiEndpointAccessibilityChecker.java similarity index 98% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiEndpointAccessibilityChecker.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiEndpointAccessibilityChecker.java index f7ee44be39..35cf44854b 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiEndpointAccessibilityChecker.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiEndpointAccessibilityChecker.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import org.eclipse.che.api.core.ApiException; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiEndpointProvider.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiEndpointProvider.java similarity index 95% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiEndpointProvider.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiEndpointProvider.java index 8859f9db36..a213a78a21 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiEndpointProvider.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiEndpointProvider.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import com.google.common.base.Strings; diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiServletModule.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiServletModule.java similarity index 97% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiServletModule.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiServletModule.java index 2503d57c2b..b5502558c3 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/ApiServletModule.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/ApiServletModule.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import com.google.common.collect.ImmutableMap; import com.google.inject.servlet.ServletModule; diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/EventBusURLProvider.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/EventBusURLProvider.java similarity index 96% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/EventBusURLProvider.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/EventBusURLProvider.java index 484c4494cb..a8c7a39c26 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/EventBusURLProvider.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/EventBusURLProvider.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import javax.inject.Inject; import javax.inject.Named; diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/MachineModule.java similarity index 97% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/MachineModule.java index aa6795173c..017f1be837 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/MachineModule.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/MachineModule.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -101,7 +101,7 @@ public class MachineModule extends AbstractModule { bind(WSocketEventBusClient.class).asEagerSingleton(); bind(String.class).annotatedWith(Names.named("event.bus.url")).toProvider(EventBusURLProvider.class); - bind(org.eclipse.che.ide.ext.java.server.ApiEndpointAccessibilityChecker.class); + bind(ApiEndpointAccessibilityChecker.class); } //it's need for WSocketEventBusClient and in the future will be replaced with the property diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/UriApiEndpointProvider.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/UriApiEndpointProvider.java similarity index 96% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/UriApiEndpointProvider.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/UriApiEndpointProvider.java index 97ee76600d..872f091734 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/UriApiEndpointProvider.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/UriApiEndpointProvider.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import javax.inject.Provider; import java.net.URI; diff --git a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/UserTokenProvider.java b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/UserTokenProvider.java similarity index 95% rename from assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/UserTokenProvider.java rename to assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/UserTokenProvider.java index 85b887006d..9b6eaee432 100644 --- a/assembly/assembly-machine-war/src/main/java/org/eclipse/che/ide/ext/java/server/UserTokenProvider.java +++ b/assembly/assembly-machine-war/src/main/java/org/eclipse/che/wsagent/server/UserTokenProvider.java @@ -8,7 +8,7 @@ * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ -package org.eclipse.che.ide.ext.java.server; +package org.eclipse.che.wsagent.server; import com.google.inject.Provider; diff --git a/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/rest/RestServiceInfo.java b/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/rest/RestServiceInfo.java new file mode 100644 index 0000000000..65123bad5c --- /dev/null +++ b/core/commons/che-core-commons-gwt/src/main/java/org/eclipse/che/ide/rest/RestServiceInfo.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.rest; + +/** + * Describe information available service like fqn of class and path. + * + * @author Vitalii Parfonov + */ +public class RestServiceInfo { + + private String fqn; + + private String regex; + + private String path; + + public RestServiceInfo(String fqn, String regex, String path) { + this.fqn = fqn; + this.regex = regex; + this.path = path; + } + + /** + * FQN of REST service class + * @return fqn + */ + public String getFqn() { + return fqn; + } + + /** + * Regular expressions for URI pattern. + * @return + */ + public String getRegex() { + return regex; + } + + /** + * Describe the Path annotation, see {@link javax.ws.rs.Path} + * @return + */ + public String getPath() { + return path; + } + + @Override + public String toString() { + return "RestServiceInfo{" + + "fqn='" + fqn + '\'' + + ", regex='" + regex + '\'' + + ", path='" + path + '\'' + + '}'; + } + +} diff --git a/core/platform-api-client-gwt/che-core-client-gwt-machine/src/main/java/org/eclipse/che/api/machine/gwt/client/WsAgentStateController.java b/core/platform-api-client-gwt/che-core-client-gwt-machine/src/main/java/org/eclipse/che/api/machine/gwt/client/WsAgentStateController.java index 74ab63b398..94488444a5 100644 --- a/core/platform-api-client-gwt/che-core-client-gwt-machine/src/main/java/org/eclipse/che/api/machine/gwt/client/WsAgentStateController.java +++ b/core/platform-api-client-gwt/che-core-client-gwt-machine/src/main/java/org/eclipse/che/api/machine/gwt/client/WsAgentStateController.java @@ -10,28 +10,39 @@ *******************************************************************************/ package org.eclipse.che.api.machine.gwt.client; +import com.google.gwt.json.client.JSONArray; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONParser; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.google.inject.name.Named; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.api.machine.gwt.client.events.WsAgentStateEvent; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper; +import org.eclipse.che.ide.rest.AsyncRequestCallback; +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.initialization.InitialLoadingInfo; import org.eclipse.che.ide.ui.loaders.initialization.LoaderPresenter; import org.eclipse.che.ide.util.loging.Log; import org.eclipse.che.ide.websocket.MessageBus; import org.eclipse.che.ide.websocket.MessageBusProvider; -import org.eclipse.che.ide.websocket.WebSocket; import org.eclipse.che.ide.websocket.events.ConnectionClosedHandler; import org.eclipse.che.ide.websocket.events.ConnectionErrorHandler; import org.eclipse.che.ide.websocket.events.ConnectionOpenedHandler; import org.eclipse.che.ide.websocket.events.WebSocketClosedEvent; +import java.util.ArrayList; +import java.util.List; + +import static org.eclipse.che.api.machine.gwt.client.WsAgentState.STARTED; +import static org.eclipse.che.api.machine.gwt.client.WsAgentState.STOPPED; import static org.eclipse.che.ide.ui.loaders.initialization.InitialLoadingInfo.Operations.WS_AGENT_BOOTING; -import static org.eclipse.che.ide.ui.loaders.initialization.OperationInfo.Status.ERROR; import static org.eclipse.che.ide.ui.loaders.initialization.OperationInfo.Status.IN_PROGRESS; import static org.eclipse.che.ide.ui.loaders.initialization.OperationInfo.Status.SUCCESS; @@ -42,72 +53,88 @@ import static org.eclipse.che.ide.ui.loaders.initialization.OperationInfo.Status @Singleton public class WsAgentStateController implements ConnectionOpenedHandler, ConnectionClosedHandler, ConnectionErrorHandler { - private final Timer retryConnectionTimer; - private final EventBus eventBus; - private final MessageBusProvider messageBusProvider; - private final InitialLoadingInfo initialLoadingInfo; - private final LoaderPresenter loader; + private final EventBus eventBus; + private final MessageBusProvider messageBusProvider; + private final InitialLoadingInfo initialLoadingInfo; + private final LoaderPresenter loader; + private final AsyncRequestFactory asyncRequestFactory; + private final String extPath; - private MessageBus messageBus; - private WsAgentState state; - private String wsUrl; - private int countRetry; - private AsyncCallback messageBusCallback; - private WebSocket testConnection; //use it only for testing state of ext-service + //not used now added it for future if it we will have possibility check that service available for client call + private final List availableServices; + + private MessageBus messageBus; + private WsAgentState state; + private String wsUrl; + private String wsId; + private AsyncCallback messageBusCallback; @Inject public WsAgentStateController(EventBus eventBus, LoaderPresenter loader, MessageBusProvider messageBusProvider, + AsyncRequestFactory asyncRequestFactory, + @Named("cheExtensionPath") String extPath, InitialLoadingInfo initialLoadingInfo) { this.loader = loader; this.eventBus = eventBus; this.messageBusProvider = messageBusProvider; + this.asyncRequestFactory = asyncRequestFactory; + this.extPath = extPath; this.initialLoadingInfo = initialLoadingInfo; - - retryConnectionTimer = new Timer() { - @Override - public void run() { - connect(); - countRetry--; - } - }; + availableServices = new ArrayList<>(); } - public void initialize(String wsUrl) { - this.wsUrl = wsUrl; - this.countRetry = 50; - this.state = WsAgentState.STOPPED; - + public void initialize(String wsUrl, String wsId) { + this.wsUrl = wsUrl + "/" + wsId; + this.wsId = wsId; + this.state = STOPPED; initialLoadingInfo.setOperationStatus(WS_AGENT_BOOTING.getValue(), IN_PROGRESS); - connect(); - - + checkHttpConnection(); } @Override public void onClose(WebSocketClosedEvent event) { - Log.info(getClass(), "Test WS connection closed with code " + event.getCode() + " reason: " + event.getReason() + - " workspace agent started well "); + Log.info(getClass(), "Test WS connection closed with code " + event.getCode() + " reason: " + event.getReason()); + if (state.equals(STARTED)) { + state = STOPPED; + eventBus.fireEvent(WsAgentStateEvent.createWsAgentStoppedEvent()); + } } @Override public void onError() { - if (countRetry > 0) { - retryConnectionTimer.schedule(1000); - } else { - state = WsAgentState.STOPPED; - initialLoadingInfo.setOperationStatus(WS_AGENT_BOOTING.getValue(), ERROR); - loader.hide(); + Log.info(getClass(), "Test WS connection error"); + if (state.equals(STARTED)) { + state = STOPPED; eventBus.fireEvent(WsAgentStateEvent.createWsAgentStoppedEvent()); } } @Override public void onOpen() { - testConnection.close(); //close testing connection now we know ws-agent already start - messageBus = messageBusProvider.createMachineMessageBus(wsUrl); - state = WsAgentState.STARTED; + messageBus.removeOnOpenHandler(this); + MessageBus.ReadyState readyState = messageBus.getReadyState(); + Log.info(getClass(), readyState.toString()); + //need to make sure ready state equals 1 (OPEN) in same situations after opening it still equals 0 (CONNECTING) + if (!readyState.equals(MessageBus.ReadyState.OPEN)) { + new Timer() { + @Override + public void run() { + Log.info(getClass(), messageBus.getReadyState()); + if (messageBus.getReadyState().equals(MessageBus.ReadyState.OPEN)) { + cancel(); + started(); + } + } + }.scheduleRepeating(100); + } else { + started(); + } + } + + private void started() { + state = STARTED; initialLoadingInfo.setOperationStatus(WS_AGENT_BOOTING.getValue(), SUCCESS); loader.hide(); @@ -117,6 +144,8 @@ public class WsAgentStateController implements ConnectionOpenedHandler, Connecti eventBus.fireEvent(WsAgentStateEvent.createWsAgentStartedEvent()); } + + public WsAgentState getState() { return state; } @@ -134,10 +163,48 @@ public class WsAgentStateController implements ConnectionOpenedHandler, Connecti }); } - private void connect() { - testConnection = WebSocket.create(wsUrl); - testConnection.setOnOpenHandler(this); - testConnection.setOnCloseHandler(this); - testConnection.setOnErrorHandler(this); + /** + * Goto checking HTTP connection via getting all registered REST Services + */ + private void checkHttpConnection() { + asyncRequestFactory.createGetRequest(extPath + "/" + wsId + "/").send(new AsyncRequestCallback(new StringUnmarshaller()) { + @Override + protected void onSuccess(String result) { + 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)); + } + checkWsConnection(); + } + } + + @Override + protected void onFailure(Throwable exception) { + Log.error(getClass(), exception.getMessage()); + new Timer() { + @Override + public void run() { + checkHttpConnection(); + } + }.schedule(1000); + } + }); + } + + /** + * Try to connect via WebSocket connection + */ + private void checkWsConnection() { + messageBus = messageBusProvider.createMachineMessageBus(wsUrl); + messageBus.addOnCloseHandler(this); + messageBus.addOnCloseHandler(this); + messageBus.addOnOpenHandler(this); + } } diff --git a/core/platform-api/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServlet.java b/core/platform-api/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServlet.java index 5312512afd..4d1ca34522 100644 --- a/core/platform-api/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServlet.java +++ b/core/platform-api/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServlet.java @@ -47,7 +47,8 @@ import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; @Singleton public class MachineExtensionProxyServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MachineExtensionProxyServlet.class); - private static final Pattern EXTENSION_API_URI = Pattern.compile(".*?/ext/[^/]+/(?[^/]+)/?.*"); + private static final String WORKSPACE_ID_PATTERN = "([^/]+)"; + private static final Pattern EXTENSION_API_URI = Pattern.compile(".*?/ext/([^/]+/" + WORKSPACE_ID_PATTERN + "/?.*|" + WORKSPACE_ID_PATTERN + "/)"); private final int extServicesPort; private final MachineManager machineManager; @@ -119,7 +120,7 @@ public class MachineExtensionProxyServlet extends HttpServlet { String workspaceId; final Matcher matcher = EXTENSION_API_URI.matcher(req.getRequestURI()); if (matcher.matches()) { - workspaceId = matcher.group("workspaceId"); + workspaceId = matcher.group(2) != null ? matcher.group(2) : matcher.group(3); } else { throw new NotFoundException("No workspace id is found in request."); } @@ -131,9 +132,21 @@ public class MachineExtensionProxyServlet extends HttpServlet { } final UriBuilder uriBuilder = UriBuilder.fromUri(server.getUrl()) - .replacePath(req.getRequestURI()) .replaceQuery(req.getQueryString()); + //check contain original url path to service before wsId + //if yes we just change host and port and forward to the ws-agent + //if not we need to cut wsId from URL + if (matcher.group(2) == null) { + // here we cut workspaceId from extension API Url for getting access to org.eclipse.che.api.core.rest.ApiInfoService + // e.g localhost:8080/ide/ext/{wsid} -> localhost:{extServicesPort}/ide/ext/ + String originUri = req.getRequestURI(); + final int indexOf = originUri.lastIndexOf('/', originUri.length() - 2); + uriBuilder.replacePath(originUri.substring(0, indexOf + 1)); + } else { + uriBuilder.replacePath(req.getRequestURI()); + } + return uriBuilder.build().toString(); } diff --git a/core/platform-api/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServletTest.java b/core/platform-api/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServletTest.java index a8c49788b4..dd2a6de852 100644 --- a/core/platform-api/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServletTest.java +++ b/core/platform-api/che-core-api-machine/src/test/java/org/eclipse/che/api/machine/server/proxy/MachineExtensionProxyServletTest.java @@ -219,6 +219,32 @@ public class MachineExtensionProxyServletTest { } } + @Test + public void shouldBeAbleToProxyWithout() throws Exception { + final String path = "/api/ext/" + WORKSPACE_ID + "/"; + + final String defaultContextPath = contextHandler.getContextPath(); + try { + contextHandler.setContextPath("/api/ext/"); + + MockHttpServletRequest mockRequest = + new MockHttpServletRequest(PROXY_ENDPOINT + path, + new ByteArrayInputStream(new byte[0]), + 0, + "GET", + defaultHeaders); + + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + + proxyServlet.service(mockRequest, mockResponse); + + assertEquals(mockResponse.getStatus(), 200); + assertEquals(extensionApiRequest.uri, "/api/ext/"); + } finally { + contextHandler.setContextPath(defaultContextPath); + } + } + @Test public void shouldProxyWithQueryString() throws Exception { final String query = "key1=value1&key2=value2&key2=value3"; diff --git a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java index 391cb00be7..b443904c50 100644 --- a/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java +++ b/plugins/plugin-machine/che-plugin-machine-ext-client/src/main/java/org/eclipse/che/ide/extension/machine/client/machine/MachineManagerImpl.java @@ -281,7 +281,7 @@ public class MachineManagerImpl implements MachineManager, WorkspaceStoppedHandl appContext.setDevMachineId(machineId); appContext.setProjectsRoot(machineDto.getRuntime().projectsRoot()); devMachine = entityFactory.createMachine(machineDto); - wsAgentStateController.initialize(devMachine.getWsServerExtensionsUrl() + "/" + appContext.getWorkspaceId()); + wsAgentStateController.initialize(devMachine.getWsServerExtensionsUrl(), appContext.getWorkspaceId()); } }); }