CODENVY-132 FactoryWorkspaceComponent is reusing a previously created workspace

Change-Id: I4d3d4cd9693b1443d4aeebd9148a9668679681bb
Signed-off-by: Florent BENOIT <fbenoit@codenvy.com>
6.19.x
Florent BENOIT 2016-02-25 10:34:19 +01:00
parent db2aac7f40
commit 6aad4e3549
3 changed files with 55 additions and 103 deletions

View File

@ -546,6 +546,18 @@ public interface CoreLocalizationConstant extends Messages {
@Key("workspace.config.undefined")
String workspaceConfigUndefined();
@Key("workspace.id.undefined")
String workspaceIdUndefined();
@Key("workspace.get.failed")
String workspaceGetFailed();
@Key("workspace.not.ready")
String workspaceNotReady(String workspaceId);
@Key("workspace.not.running")
String workspaceNotRunning();
@Key("start.ws.error.title")
String startWsErrorTitle();

View File

@ -16,25 +16,23 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.core.model.workspace.WorkspaceStatus;
import org.eclipse.che.api.factory.gwt.client.FactoryServiceClient;
import org.eclipse.che.api.factory.shared.dto.Factory;
import org.eclipse.che.api.machine.gwt.client.MachineManager;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.FunctionException;
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.js.Promises;
import org.eclipse.che.api.workspace.gwt.client.WorkspaceServiceClient;
import org.eclipse.che.api.workspace.shared.dto.UsersWorkspaceDto;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceConfigDto;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.actions.WorkspaceSnapshotCreator;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.component.Component;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.preferences.PreferencesManager;
import org.eclipse.che.ide.api.component.Component;
import org.eclipse.che.ide.context.BrowserQueryFieldRenderer;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.rest.AsyncRequestCallback;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
@ -43,27 +41,22 @@ 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.MessageBusProvider;
import org.eclipse.che.ide.context.BrowserQueryFieldRenderer;
import org.eclipse.che.ide.workspace.create.CreateWorkspacePresenter;
import org.eclipse.che.ide.workspace.start.StartWorkspacePresenter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
/**
* Retrieves specified factory, and creates and/or starts workspace configured in it.
* Retrieves specified factory, and reuse previously created workspace for this factory.
*
* @author Max Shaposhnik
* @author Florent Benoit
*/
@Singleton
public class FactoryWorkspaceComponent extends WorkspaceComponent implements Component {
private static final String FACTORY_ID_ATTRIBUTE = "factoryId";
private final FactoryServiceClient factoryServiceClient;
private Factory factory;
private String workspaceId;
@Inject
public FactoryWorkspaceComponent(WorkspaceServiceClient workspaceServiceClient,
@ -108,12 +101,17 @@ public class FactoryWorkspaceComponent extends WorkspaceComponent implements Com
public void start(final Callback<Component, Exception> callback) {
this.callback = callback;
String factoryParams = browserQueryFieldRenderer.getParameterFromURLByName("factory");
// get workspace ID to use dedicated workspace for this factory
this.workspaceId = browserQueryFieldRenderer.getParameterFromURLByName("workspaceId");
factoryServiceClient.getFactory(factoryParams, true,
new AsyncRequestCallback<Factory>(dtoUnmarshallerFactory.newUnmarshaller(Factory.class)) {
@Override
protected void onSuccess(Factory result) {
factory = result;
appContext.setFactory(result);
// get workspace
tryStartWorkspace();
}
@ -127,108 +125,46 @@ public class FactoryWorkspaceComponent extends WorkspaceComponent implements Com
@Override
public void tryStartWorkspace() {
final WorkspaceConfigDto workspaceConfigDto = factory.getWorkspace();
if (workspaceConfigDto == null) {
notificationManager.notify(locale.failedToLoadFactory(), locale.workspaceConfigUndefined(), FAIL, true);
if (this.workspaceId == null) {
notificationManager.notify(locale.failedToLoadFactory(), locale.workspaceIdUndefined(), FAIL, true);
return;
}
getWorkspaceToStart().then(startWorkspace()).catchError(new Operation<PromiseError>() {
getWorkspaceToStart().then(checkWorkspaceIsStarted()).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
notificationManager.notify(locale.workspaceNotReady(workspaceId), locale.workspaceGetFailed(), FAIL, true);
Log.error(getClass(), arg.getMessage());
}
});
}
/**
* Gets {@link Promise} of workspace according to {@code factory} {@link org.eclipse.che.api.factory.shared.dto.Policies}.
* <p/>
* <p>Return policy for workspace:
* <p><i>perClick</i> - every click from any user always creates a new workspace every time and if policy is not specified<br/>
* it will be used by default.
* <p/>
* <p><i>perUser</i> - create one workspace for a user, a 2nd click from same user reloads the same workspace.
* <p/>
* <p><i>perAccount</i> - only create workspace for all users. A 2nd click from any user reloads the same workspace<br/>
* Note that if location = owner, then only 1 workspace ever is created. If location = acceptor<br/>
* it's one workspace for each unique user.
* Checks if specified workspace has {@link WorkspaceStatus} which is {@code RUNNING}
*/
protected Operation<UsersWorkspaceDto> checkWorkspaceIsStarted() {
return new Operation<UsersWorkspaceDto>() {
@Override
public void apply(UsersWorkspaceDto workspace) throws OperationException {
if (!RUNNING.equals(workspace.getStatus())) {
notificationManager.notify(locale.failedToLoadFactory(), locale.workspaceNotRunning(), FAIL, true);
throw new OperationException(locale.workspaceNotRunning());
} else {
startWorkspace().apply(workspace);
}
}
};
}
/**
* Gets {@link Promise} of workspace according to workspace ID specified in parameter.
*/
private Promise<UsersWorkspaceDto> getWorkspaceToStart() {
final WorkspaceConfigDto workspaceConfigDto = factory.getWorkspace();
final String policy = factory.getPolicies() == null ? "perClick" : factory.getPolicies().getCreate();
switch (policy) {
case "perUser":
return getWorkspaceByConditionOrCreateNew(workspaceConfigDto, new Function<UsersWorkspaceDto, Boolean>() {
@Override
public Boolean apply(UsersWorkspaceDto existWs) throws FunctionException {
return factory.getId().equals(existWs.getConfig().getAttributes().get(FACTORY_ID_ATTRIBUTE));
}
});
case "perAccount":
return getWorkspaceByConditionOrCreateNew(workspaceConfigDto, new Function<UsersWorkspaceDto, Boolean>() {
@Override
public Boolean apply(UsersWorkspaceDto arg) throws FunctionException {
//TODO rework it when account will be ready
return workspaceConfigDto.getName().equals(arg.getConfig().getName());
}
});
case "perClick":
default:
return getWorkspaceByConditionOrCreateNew(workspaceConfigDto, new Function<UsersWorkspaceDto, Boolean>() {
@Override
public Boolean apply(UsersWorkspaceDto arg) throws FunctionException {
return false;
}
});
}
// get workspace from the given id
return this.workspaceServiceClient.getUsersWorkspace(workspaceId);
}
/**
* Gets the workspace by condition which is determined by given {@link Function}
* if workspace found by condition then it will be returned in other way new workspace will be returned.
*/
private Promise<UsersWorkspaceDto> getWorkspaceByConditionOrCreateNew(final WorkspaceConfigDto workspaceConfigDto,
final Function<UsersWorkspaceDto, Boolean> condition) {
return workspaceServiceClient.getWorkspaces(0, 0)
.thenPromise(new Function<List<UsersWorkspaceDto>, Promise<UsersWorkspaceDto>>() {
@Override
public Promise<UsersWorkspaceDto> apply(List<UsersWorkspaceDto> workspaces)
throws FunctionException {
for (UsersWorkspaceDto existsWs : workspaces) {
if (condition.apply(existsWs)) {
return Promises.resolve(existsWs);
}
}
return createWorkspaceWithCounterName(workspaces, workspaceConfigDto);
}
});
}
/**
* Create workspace with counter in name and add factoryId attribute
* if workspace with specified name already exist.
*/
private Promise<UsersWorkspaceDto> createWorkspaceWithCounterName(final List<UsersWorkspaceDto> workspaces,
final WorkspaceConfigDto workspaceConfigDto) {
workspaceConfigDto.getAttributes().put(FACTORY_ID_ATTRIBUTE, factory.getId());
final Set<String> workspacesNames = new HashSet<>();
final String wsName = workspaceConfigDto.getName();
for (UsersWorkspaceDto workspace : workspaces) {
workspacesNames.add(workspace.getConfig().getName());
}
if (!workspacesNames.contains(wsName)) {
return workspaceServiceClient.create(workspaceConfigDto, null);
}
String genName = wsName;
int counter = 1;
while (workspacesNames.contains(genName)) {
genName = wsName + '-' + counter++;
}
workspaceConfigDto.withName(genName);
workspaceConfigDto.getAttributes().put(FACTORY_ID_ATTRIBUTE, factory.getId());
return workspaceServiceClient.create(workspaceConfigDto, null);
}
}

View File

@ -267,8 +267,12 @@ started.ws=Workspace is running
ext.server.started=Workspace agent started
ext.server.stopped=Workspace agent stopped
workspace.start.failed=Failed to start workspace
workspace.get.failed=Failed to get workspace
failed.to.load.factory=Failed to load factory
workspace.not.ready=Workspace {0} is not ready
workspace.not.running=Workspace is not running
workspace.config.undefined=Workspace config is not defined.
workspace.id.undefined=Workspace ID is not defined.
start.ws.error.title=Start Workspace Error
start.ws.error.content=Could not start workspace {0}. Reason: {1}