From 4088de7e58045e3a0d50d22d23ed601cc80a7f86 Mon Sep 17 00:00:00 2001 From: Dmitry Kuleshov Date: Mon, 23 May 2016 16:05:59 +0300 Subject: [PATCH] CHE-1143: fixed minor synchronization issue during removal of several projects Signed-off-by: Dmitry Kuleshov --- .../vfs/impl/file/LocalVirtualFileSystem.java | 2 +- .../project/server/ProjectServiceTest.java | 93 +++++++++++++++---- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/LocalVirtualFileSystem.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/LocalVirtualFileSystem.java index 9078b7674bd..e125485c65a 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/LocalVirtualFileSystem.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/LocalVirtualFileSystem.java @@ -243,7 +243,7 @@ private boolean isVfsServicePath(Path path) { return newArrayList(path.elements()).contains(".vfs"); } - List getChildren(LocalVirtualFile parent, VirtualFileFilter filter) throws ServerException { + synchronized List getChildren(LocalVirtualFile parent, VirtualFileFilter filter) throws ServerException { if (parent.isFolder()) { final List children = doGetChildren(parent, DOT_VFS_DIR_FILTER, filter); Collections.sort(children); diff --git a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java index 780f311d712..76ffacacd55 100644 --- a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java +++ b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java @@ -92,11 +92,17 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -219,7 +225,7 @@ public void setUp() throws Exception { FileWatcherNotificationHandler fileWatcherNotificationHandler = new DefaultFileWatcherNotificationHandler(vfsProvider); FileTreeWatcher fileTreeWatcher = new FileTreeWatcher(root, new HashSet<>(), fileWatcherNotificationHandler); - pm = new ProjectManager(vfsProvider, null, ptRegistry, projectRegistry, phRegistry, + pm = new ProjectManager(vfsProvider, new EventService(), ptRegistry, projectRegistry, phRegistry, importerRegistry, fileWatcherNotificationHandler, fileTreeWatcher, workspaceHolder); pm.initWatcher(); @@ -227,31 +233,15 @@ public void setUp() throws Exception { //List modules = new ArrayList<>(); - final ProjectConfigDto testProjectConfigMock = mock(ProjectConfigDto.class); - when(testProjectConfigMock.getPath()).thenReturn("/my_project"); - when(testProjectConfigMock.getName()).thenReturn("my_project"); - when(testProjectConfigMock.getDescription()).thenReturn("my test project"); - when(testProjectConfigMock.getType()).thenReturn("my_project_type"); -// when(testProjectConfigMock.getModules()).thenReturn(modules); - when(testProjectConfigMock.getSource()).thenReturn(DtoFactory.getInstance().createDto(SourceStorageDto.class)); -// when(testProjectConfigMock.findModule(anyString())).thenReturn(testProjectConfigMock); - - Map> attr = new HashMap<>(); - for (Attribute attribute : myProjectType.getAttributes()) { - attr.put(attribute.getName(), attribute.getValue().getList()); - } - when(testProjectConfigMock.getAttributes()).thenReturn(attr); - projects = new ArrayList<>(); - projects.add(testProjectConfigMock); + addMockedProjectConfigDto(myProjectType, "my_project"); + when(httpJsonRequestFactory.fromLink(any())).thenReturn(httpJsonRequest); when(httpJsonRequest.request()).thenReturn(httpJsonResponse); when(httpJsonResponse.asDto(WorkspaceDto.class)).thenReturn(usersWorkspaceMock); when(usersWorkspaceMock.getConfig()).thenReturn(workspaceConfigMock); when(workspaceConfigMock.getProjects()).thenReturn(projects); - pm.createProject(testProjectConfigMock, null); - // verify(httpJsonRequestFactory).fromLink(eq(DtoFactory.newDto(Link.class) // .withHref(apiEndpoint + "/workspace/" + workspace + "/project") // .withMethod(PUT))); @@ -288,6 +278,27 @@ public Set getSingletons() { env = org.eclipse.che.commons.env.EnvironmentContext.getCurrent(); } + private void addMockedProjectConfigDto(org.eclipse.che.api.project.server.type.ProjectTypeDef myProjectType, String projectName) + throws ForbiddenException, ServerException, NotFoundException, ConflictException { + final ProjectConfigDto testProjectConfigMock = mock(ProjectConfigDto.class); + when(testProjectConfigMock.getPath()).thenReturn("/" + projectName); + when(testProjectConfigMock.getName()).thenReturn(projectName); + when(testProjectConfigMock.getDescription()).thenReturn("my test project"); + when(testProjectConfigMock.getType()).thenReturn("my_project_type"); + when(testProjectConfigMock.getSource()).thenReturn(DtoFactory.getInstance().createDto(SourceStorageDto.class)); + // when(testProjectConfigMock.getModules()).thenReturn(modules); + // when(testProjectConfigMock.findModule(anyString())).thenReturn(testProjectConfigMock); + + Map> attr = new HashMap<>(); + for (Attribute attribute : myProjectType.getAttributes()) { + attr.put(attribute.getName(), attribute.getValue().getList()); + } + when(testProjectConfigMock.getAttributes()).thenReturn(attr); + + projects.add(testProjectConfigMock); + + pm.createProject(testProjectConfigMock, null); + } @Test @SuppressWarnings("unchecked") @@ -927,6 +938,50 @@ public void testDeleteProject() throws Exception { pm.getProject("my_project"); } + @Test + public void testDeleteProjectsConcurrently() throws Exception { + int threadNumber = 100; + ExecutorService executor = Executors.newFixedThreadPool(threadNumber); + CountDownLatch countDownLatch = new CountDownLatch(threadNumber); + List> futures = new LinkedList<>(); + + + for (int i = 0; i < threadNumber; i++) { + addMockedProjectConfigDto(ptRegistry.getProjectType("my_project_type"), "my_project_name" + i); + } + + IntStream.range(0, threadNumber).forEach( + i -> { + futures.add(executor.submit(() -> { + countDownLatch.countDown(); + countDownLatch.await(); + + try { + return launcher.service(DELETE, + "http://localhost:8080/api/project/my_project_name" + i, + "http://localhost:8080/api", null, null, null); + } catch (Exception e) { + throw new IllegalStateException(e); + } + })); + } + ); + + boolean isNotDone; + do { + isNotDone = false; + for (Future future : futures) { + if (!future.isDone()) { + isNotDone = true; + } + } + } while (isNotDone); + + for (Future future : futures) { + assertEquals(future.get().getStatus(), 204, "Error: " + future.get().getEntity()); + } + } + @Test public void testCopyFile() throws Exception { RegisteredProject myProject = pm.getProject("my_project");