[WIP] CHE-11: VFS event handling system (#1634)
CHE-11: VFS event handling system initial commit6.19.x
parent
e11c8e5406
commit
435bbd5505
|
|
@ -74,6 +74,10 @@
|
|||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-project</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-project-shared</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.che.core</groupId>
|
||||
<artifactId>che-core-api-workspace-shared</artifactId>
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@ import com.google.inject.name.Named;
|
|||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.notification.EventSubscriber;
|
||||
import org.eclipse.che.api.project.server.notification.ProjectItemModifiedEvent;
|
||||
import org.eclipse.che.api.project.shared.dto.event.PomModifiedEventDto;
|
||||
import org.eclipse.che.commons.schedule.executor.ThreadPullLauncher;
|
||||
import org.eclipse.che.ide.maven.tools.Model;
|
||||
import org.eclipse.che.plugin.maven.server.core.EclipseWorkspaceProvider;
|
||||
import org.eclipse.che.plugin.maven.server.core.MavenWorkspace;
|
||||
import org.eclipse.che.ide.maven.tools.Model;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
|
@ -71,6 +72,16 @@ public class PomChangeListener {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
eventService.subscribe(new EventSubscriber<PomModifiedEventDto>() {
|
||||
@Override
|
||||
public void onEvent(PomModifiedEventDto event) {
|
||||
String eventPath = event.getPath();
|
||||
if (pomIsValid(eventPath)) {
|
||||
projectToUpdate.add(new Path(eventPath).removeLastSegments(1).toOSString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean pomIsValid(String path) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.project.shared.dto.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.dto.shared.DTO;
|
||||
|
||||
/**
|
||||
* To transfer branch name after git checkout operation
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@DTO
|
||||
public interface GitBranchCheckoutEventDto {
|
||||
String getBranchName();
|
||||
|
||||
GitBranchCheckoutEventDto withBranchName(String branchName);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.project.shared.dto.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.dto.shared.DTO;
|
||||
|
||||
/**
|
||||
* To transfer modified POM path
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@DTO
|
||||
public interface PomModifiedEventDto {
|
||||
String getPath();
|
||||
|
||||
PomModifiedEventDto withPath(String path);
|
||||
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
package org.eclipse.che.api.project.server;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
|
|
@ -26,6 +27,12 @@ import org.eclipse.che.api.vfs.VirtualFileSystemProvider;
|
|||
import org.eclipse.che.api.vfs.impl.file.DefaultFileWatcherNotificationHandler;
|
||||
import org.eclipse.che.api.vfs.impl.file.FileWatcherNotificationHandler;
|
||||
import org.eclipse.che.api.vfs.impl.file.LocalVirtualFileSystemProvider;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.GitCheckoutHiEventDetector;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.HiEventDetector;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.HiEventService;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.LoEventListener;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.LoEventService;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.PomModifiedHiEventDetector;
|
||||
import org.eclipse.che.api.vfs.search.MediaTypeFilter;
|
||||
import org.eclipse.che.api.vfs.search.SearcherProvider;
|
||||
import org.eclipse.che.api.vfs.search.impl.FSLuceneSearcherProvider;
|
||||
|
|
@ -72,5 +79,15 @@ public class ProjectApiModule extends AbstractModule {
|
|||
bind(VirtualFileSystemProvider.class).to(LocalVirtualFileSystemProvider.class);
|
||||
|
||||
bind(FileWatcherNotificationHandler.class).to(DefaultFileWatcherNotificationHandler.class);
|
||||
|
||||
bind(LoEventListener.class);
|
||||
bind(LoEventService.class);
|
||||
bind(HiEventService.class);
|
||||
|
||||
Multibinder<HiEventDetector<?>> highLevelVfsEventDetectorMultibinder =
|
||||
Multibinder.newSetBinder(binder(), new TypeLiteral<HiEventDetector<?>>() {
|
||||
});
|
||||
highLevelVfsEventDetectorMultibinder.addBinding().to(PomModifiedHiEventDetector.class);
|
||||
highLevelVfsEventDetectorMultibinder.addBinding().to(GitCheckoutHiEventDetector.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,18 +34,16 @@ import org.eclipse.che.api.project.server.type.ProjectTypeRegistry;
|
|||
import org.eclipse.che.api.project.server.type.ProjectTypeResolution;
|
||||
import org.eclipse.che.api.project.server.type.ValueStorageException;
|
||||
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
|
||||
import org.eclipse.che.api.project.shared.dto.event.VfsWatchEvent;
|
||||
import org.eclipse.che.api.vfs.Path;
|
||||
import org.eclipse.che.api.vfs.VirtualFile;
|
||||
import org.eclipse.che.api.vfs.VirtualFileFilter;
|
||||
import org.eclipse.che.api.vfs.VirtualFileSystem;
|
||||
import org.eclipse.che.api.vfs.VirtualFileSystemProvider;
|
||||
import org.eclipse.che.api.vfs.impl.file.FileTreeWatcher;
|
||||
import org.eclipse.che.api.vfs.impl.file.FileWatcherNotificationHandler;
|
||||
import org.eclipse.che.api.vfs.impl.file.FileWatcherNotificationListener;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.LoEvent;
|
||||
import org.eclipse.che.api.vfs.search.Searcher;
|
||||
import org.eclipse.che.api.vfs.search.SearcherProvider;
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -109,16 +107,22 @@ public final class ProjectManager {
|
|||
|
||||
@PostConstruct
|
||||
void initWatcher() throws IOException {
|
||||
FileWatcherNotificationListener defaultListener = new FileWatcherNotificationListener(VirtualFileFilter.ACCEPT_ALL) {
|
||||
@Override
|
||||
public void onFileWatcherEvent(VirtualFile virtualFile, FileWatcherEventType eventType) {
|
||||
LOG.debug("FS event detected: " + eventType + " " + virtualFile.getPath().toString() + " " + virtualFile.isFile());
|
||||
eventService.publish(DtoFactory.newDto(VfsWatchEvent.class)
|
||||
.withPath(virtualFile.getPath().toString())
|
||||
.withFile(virtualFile.isFile())
|
||||
.withType(eventType));
|
||||
}
|
||||
};
|
||||
FileWatcherNotificationListener defaultListener =
|
||||
new FileWatcherNotificationListener(file -> !(file.getPath().toString().contains(".codenvy")
|
||||
|| file.getPath().toString().contains(".#"))) {
|
||||
@Override
|
||||
public void onFileWatcherEvent(VirtualFile virtualFile, FileWatcherEventType eventType) {
|
||||
LOG.debug("FS event detected: " + eventType + " " + virtualFile.getPath().toString() + " " + virtualFile.isFile());
|
||||
eventService.publish(LoEvent.newInstance()
|
||||
.withPath(virtualFile.getPath().toString())
|
||||
.withName(virtualFile.getName())
|
||||
.withItemType(virtualFile.isFile()
|
||||
? LoEvent.ItemType.FILE
|
||||
: LoEvent.ItemType.DIR)
|
||||
.withTime(System.currentTimeMillis())
|
||||
.withEventType(eventType));
|
||||
}
|
||||
};
|
||||
fileWatchNotifier.addNotificationListener(defaultListener);
|
||||
try {
|
||||
fileWatcher.startup();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.vfs.Path;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeNode.newInstance;
|
||||
|
||||
/**
|
||||
* Helper for event tree related operations.
|
||||
*
|
||||
* @see {@link EventTreeNode}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
public class EventTreeHelper {
|
||||
|
||||
/**
|
||||
* Adds corresponding event to an event tree.
|
||||
* <p>
|
||||
* If event's tree parents are not yet in the tree they are implicitly created
|
||||
* with default values and no events. For tree nodes created in such way call
|
||||
* of {@link EventTreeNode#modificationOccurred()} method always return false.
|
||||
* </p>
|
||||
* @param root root node of the tree, node where event's absolute path starts
|
||||
*
|
||||
* @param loEvent event to be added
|
||||
*/
|
||||
public static void addEventAndCreatePrecedingNodes(EventTreeNode root, LoEvent loEvent) {
|
||||
traverseAndCreate(root, Path.of(loEvent.getPath()))
|
||||
.withEvent(loEvent)
|
||||
.withPath(loEvent.getPath())
|
||||
.withType(loEvent.getItemType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a tree node according to a relative path starting from a predefined tree node.
|
||||
*
|
||||
* @param root node where relative path starts
|
||||
* @param relativePath relative path
|
||||
*
|
||||
* @return node if node with such path exists otherwise {@code null}
|
||||
*/
|
||||
public static Optional<EventTreeNode> getTreeNode(EventTreeNode root, String relativePath) {
|
||||
Optional<EventTreeNode> current = Optional.of(root);
|
||||
|
||||
if (relativePath.startsWith(separator)) {
|
||||
relativePath = relativePath.substring(1);
|
||||
}
|
||||
|
||||
if (relativePath.endsWith(separator)) {
|
||||
relativePath = relativePath.substring(0, relativePath.length() - 1);
|
||||
}
|
||||
|
||||
for (String segment : relativePath.split(separator)) {
|
||||
if (current.isPresent()) {
|
||||
current = current.get().getChild(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
private static EventTreeNode traverseAndCreate(EventTreeNode root, Path path) {
|
||||
EventTreeNode current = root;
|
||||
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
final String name = path.element(i);
|
||||
final EventTreeNode parent = current;
|
||||
final Optional<EventTreeNode> childOptional = current.getChild(name);
|
||||
|
||||
current = childOptional.orElseGet(() -> newInstance().withName(name).withParent(parent));
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.stream.Stream.concat;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.DIR;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.FILE;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.UNDEFINED;
|
||||
|
||||
/**
|
||||
* Virtual file system event tree is a data structure to store low level VFS
|
||||
* events in a specific manner. Events are stored in a such way that an event
|
||||
* tree corresponds to a file system items tree but includes only those tree
|
||||
* branches that contain modified file system items.
|
||||
* <p>
|
||||
* By design event tree represents a time-based event segment. All events
|
||||
* close enough (in terms of time line) to each other are included to the
|
||||
* tree.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class uses {@link LinkedHashMap} to store an event chain, not to
|
||||
* lose data when we have several sequential modifications of a single
|
||||
* tree item.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: for convenience there is a predefined event tree root node - '/',
|
||||
* which is defined by {@link EventTreeNode#ROOT_NODE_NAME} constant.
|
||||
* All trees must be started from that node using corresponding factory
|
||||
* method {@link EventTreeNode#newRootInstance()}
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
public class EventTreeNode {
|
||||
|
||||
/**
|
||||
* Tree root node name. All properly constructed trees must be started from this node.
|
||||
*/
|
||||
private static final String ROOT_NODE_NAME = "/";
|
||||
|
||||
private List<EventTreeNode> children;
|
||||
private String name;
|
||||
private String path;
|
||||
private ItemType type;
|
||||
/**
|
||||
* Event chain to store all events occurred within a single time
|
||||
* segment with this event tree node instance.
|
||||
* Key - timestamp in millis, value - event type
|
||||
*/
|
||||
private Map<Long, FileWatcherEventType> events;
|
||||
|
||||
private EventTreeNode() {
|
||||
this.events = new LinkedHashMap<>();
|
||||
this.children = new LinkedList<>();
|
||||
this.type = UNDEFINED;
|
||||
}
|
||||
|
||||
public static EventTreeNode newRootInstance() {
|
||||
return EventTreeNode.newInstance().withName(ROOT_NODE_NAME);
|
||||
}
|
||||
|
||||
public static EventTreeNode newInstance() {
|
||||
return new EventTreeNode();
|
||||
}
|
||||
|
||||
public EventTreeNode withName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventTreeNode withParent(EventTreeNode parent) {
|
||||
parent.withChild(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public EventTreeNode withChild(EventTreeNode child) {
|
||||
this.children.add(child);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventTreeNode withEvent(LoEvent loEvent) {
|
||||
this.events.put(loEvent.getTime(), loEvent.getEventType());
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventTreeNode withPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventTreeNode withType(ItemType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public ItemType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<EventTreeNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Optional<EventTreeNode> getChild(String name) {
|
||||
for (EventTreeNode node : children) {
|
||||
if (node.getName().equals(name)) {
|
||||
return Optional.of(node);
|
||||
}
|
||||
}
|
||||
|
||||
return empty();
|
||||
}
|
||||
|
||||
public Optional<EventTreeNode> getFirstChild() {
|
||||
if (children.isEmpty()) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
return Optional.of(children.get(0));
|
||||
}
|
||||
|
||||
public Map<Long, FileWatcherEventType> getEvents() {
|
||||
return events;
|
||||
}
|
||||
|
||||
public FileWatcherEventType getLastEventType() {
|
||||
final List<Map.Entry<Long, FileWatcherEventType>> entryList = new ArrayList<>(events.entrySet());
|
||||
final Map.Entry<Long, FileWatcherEventType> lastEntry = entryList.get(entryList.size() - 1);
|
||||
|
||||
return lastEntry.getValue();
|
||||
}
|
||||
|
||||
public boolean modificationOccurred() {
|
||||
return !events.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isFile() {
|
||||
return type.equals(FILE);
|
||||
}
|
||||
|
||||
public boolean isDir() {
|
||||
return type.equals(DIR);
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return ROOT_NODE_NAME.equals(name);
|
||||
}
|
||||
|
||||
public Stream<EventTreeNode> stream() {
|
||||
return concat(Stream.of(this), this.children.stream().flatMap(EventTreeNode::stream));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* Simple holder to benefit from Guice DI routines.
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
class EventTreeQueueHolder {
|
||||
private static final Logger LOG = getLogger(EventTreeQueueHolder.class);
|
||||
|
||||
private final BlockingQueue<EventTreeNode> loVfsEventQueue;
|
||||
|
||||
public EventTreeQueueHolder() {
|
||||
this.loVfsEventQueue = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
public void put(EventTreeNode loVfsEventTreeRoot) {
|
||||
try {
|
||||
loVfsEventQueue.put(loVfsEventTreeRoot);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Error trying to put an event tree to an event tree queue: {}", loVfsEventTreeRoot, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<EventTreeNode> take() {
|
||||
try {
|
||||
return Optional.of(loVfsEventQueue.take());
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Error trying to take an event tree out of an event tree queue", e);
|
||||
}
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.core.ForbiddenException;
|
||||
import org.eclipse.che.api.core.ServerException;
|
||||
import org.eclipse.che.api.project.shared.dto.event.GitBranchCheckoutEventDto;
|
||||
import org.eclipse.che.api.vfs.Path;
|
||||
import org.eclipse.che.api.vfs.VirtualFileSystemProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.regex.Pattern.compile;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.HiEvent.Category.UNDEFINED;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* Detects if there was a .git/HEAD file modification, which is a sign of git branch
|
||||
* checkout operation, though in some rare cases it simply shows that the head of
|
||||
* current branch is changed.
|
||||
* <p>
|
||||
* By the moment of this class creation those situations are considered rare
|
||||
* enough to ignore false detections.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is designed to detect only HEAD file MODIFICATION, which means that it will
|
||||
* not trigger if those files are CREATED, DELETED, etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* This very implementation works only with git repositories initialized in
|
||||
* the project root folder.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
public class GitCheckoutHiEventDetector implements HiEventDetector<GitBranchCheckoutEventDto> {
|
||||
private static final Logger LOG = getLogger(GitCheckoutHiEventDetector.class);
|
||||
|
||||
private static final String GIT_DIR = ".git";
|
||||
private static final String HEAD_FILE = "HEAD";
|
||||
private static final String GIT_OPERATION_WS_CHANNEL = "git-operations-channel";
|
||||
private static final int PRIORITY = 50;
|
||||
private static final Pattern PATTERN = compile("ref: refs" + separator + "heads" + separator);
|
||||
|
||||
private final VirtualFileSystemProvider virtualFileSystemProvider;
|
||||
private final HiEventBroadcaster broadcaster;
|
||||
|
||||
@Inject
|
||||
public GitCheckoutHiEventDetector(VirtualFileSystemProvider virtualFileSystemProvider,
|
||||
HiEventClientBroadcaster highLevelVfsEventClientBroadcaster) {
|
||||
this.virtualFileSystemProvider = virtualFileSystemProvider;
|
||||
this.broadcaster = highLevelVfsEventClientBroadcaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<HiEvent<GitBranchCheckoutEventDto>> detect(EventTreeNode eventTreeNode) {
|
||||
if (!eventTreeNode.isRoot() || eventTreeNode.getChildren().isEmpty()) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
final Optional<EventTreeNode> headFile = eventTreeNode.getFirstChild()
|
||||
.flatMap(o -> o.getChild(GIT_DIR))
|
||||
.flatMap(o -> o.getChild(HEAD_FILE));
|
||||
|
||||
if (headFile.isPresent()
|
||||
&& headFile.get().modificationOccurred()
|
||||
&& MODIFIED.equals(headFile.get().getLastEventType())) {
|
||||
|
||||
final GitBranchCheckoutEventDto dto = newDto(GitBranchCheckoutEventDto.class).withBranchName(getBranchName(headFile.get()));
|
||||
|
||||
return Optional.of(HiEvent.newInstance(GitBranchCheckoutEventDto.class)
|
||||
.withCategory(UNDEFINED.withPriority(PRIORITY))
|
||||
.withBroadcaster(broadcaster)
|
||||
.withChannel(GIT_OPERATION_WS_CHANNEL)
|
||||
.withDto(dto));
|
||||
} else {
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
||||
private String getBranchName(EventTreeNode file) {
|
||||
try {
|
||||
final String result = virtualFileSystemProvider.getVirtualFileSystem()
|
||||
.getRoot()
|
||||
.getChild(Path.of(file.getPath()))
|
||||
.getContentAsString();
|
||||
return PATTERN.split(result)[1];
|
||||
} catch (ServerException | ForbiddenException e) {
|
||||
LOG.error("Error trying to read {} file and broadcast it", file.getPath(), e);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* High level virtual file system event implementation. Unlike {@link LoEvent}
|
||||
* it is to represent not physical but logical events. In terms of file system
|
||||
* logical events are those that may be caused by one or more physical events.
|
||||
* In other words sets of low level VFS events result in high level VFS events.
|
||||
* <p>
|
||||
* Small example: creation of project's root folder ({@link LoEvent})
|
||||
* along with populating it with content (creating new files and folders -
|
||||
* also {@link LoEvent}) indicates that most likely a project is imported.
|
||||
* And project import is a {@link HiEvent}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: We consider that the user of this class is responsible for setting
|
||||
* proper broadcasters and (if it is required) all additional information
|
||||
* (e.g. web socket channel).
|
||||
* </p>
|
||||
* <p>
|
||||
* Each high level VFS event instance can have arbitrary number (but not less
|
||||
* than one) of {@link HiEventBroadcaster} defined. By design an instance
|
||||
* of {@link HiEventDetector} which is responsible for creating of event
|
||||
* is also responsible for definition of a list of corresponding broadcasters.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
public class HiEvent<T> {
|
||||
/**
|
||||
* Should be a plain DTO object representing this event. Is designed to be
|
||||
* further used both for client-side and server-side notifications.
|
||||
* <p>
|
||||
* Note: The implementation of DTO should fulfill all restrictions and
|
||||
* be located in {@code org.eclipse.che.api.project.shared.dto.event}
|
||||
* package
|
||||
* </p>
|
||||
*/
|
||||
private T dto;
|
||||
/**
|
||||
* Field holds category property of an event. Please see {@link Category}
|
||||
*/
|
||||
private Category category;
|
||||
private List<HiEventBroadcaster> broadcasters;
|
||||
/**
|
||||
* Web socket channel name.
|
||||
* <p>
|
||||
* It is ignored if event is not expected to be sent over web socket
|
||||
* (e.g. server side event)
|
||||
* </p>
|
||||
*/
|
||||
private String channel;
|
||||
|
||||
private HiEvent() {
|
||||
this.broadcasters = new LinkedList<>();
|
||||
}
|
||||
|
||||
public static <T> HiEvent<T> newInstance(Class<T> type) {
|
||||
return new HiEvent<>();
|
||||
}
|
||||
|
||||
public T getDto() {
|
||||
return dto;
|
||||
}
|
||||
|
||||
public HiEvent<T> withDto(T dto) {
|
||||
this.dto = dto;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public HiEvent<T> withChannel(String channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public HiEvent<T> withCategory(Category category) {
|
||||
this.category = category;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HiEvent<T> withBroadcaster(HiEventBroadcaster hiEventBroadcaster) {
|
||||
this.broadcasters.add(hiEventBroadcaster);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void broadcast() {
|
||||
broadcasters.stream().forEach(o -> o.broadcast(this));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple enumeration to represent event categorizing mechanics.
|
||||
* <p>
|
||||
* The idea is to allow only one high level event per category for
|
||||
* a single low level events set (tree snapshot). If we have several
|
||||
* high level events with the same category we are to choose the most
|
||||
* appropriate event according to its priority.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: UNDEFINED is used for all events that have no category.
|
||||
* </p>
|
||||
*/
|
||||
@Beta
|
||||
public enum Category {
|
||||
UNDEFINED,
|
||||
PROJECT_INFRASTRUCTURE;
|
||||
|
||||
private long priority;
|
||||
|
||||
public long getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public Category withPriority(long priority) {
|
||||
this.priority = priority;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
/**
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@FunctionalInterface
|
||||
public interface HiEventBroadcaster {
|
||||
void broadcast(HiEvent event);
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.vfs.impl.file.event.HiEvent.Category;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.Long.compare;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.HiEvent.Category.UNDEFINED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.HiEvent.Category.values;
|
||||
|
||||
/**
|
||||
* Splits high level event list according to their categories (see {@link Category})
|
||||
* and then broadcast categorized events with the highest priorities.
|
||||
* <p>
|
||||
* Note: broadcasters are secured for each {@link HiEvent} instance during
|
||||
* creation, so it is out of scope of this class to define broadcasters, but simply
|
||||
* to call them.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
class HiEventBroadcasterManager {
|
||||
void manageEvents(List<HiEvent> hiEvents) {
|
||||
if (hiEvents.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Category category : values()) {
|
||||
|
||||
final List<HiEvent> events = hiEvents.stream()
|
||||
.filter(o -> o.getCategory().equals(category))
|
||||
.sorted((o1, o2) -> compare(o1.getCategory().getPriority(),
|
||||
o2.getCategory().getPriority()))
|
||||
.collect(toList());
|
||||
|
||||
if (UNDEFINED.equals(category)) {
|
||||
events.stream().forEach(HiEvent::broadcast);
|
||||
} else if (!events.isEmpty()) {
|
||||
// getting the event with the highest priority
|
||||
events.get(0).broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.dto.server.DtoFactory;
|
||||
import org.everrest.websockets.WSConnectionContext;
|
||||
import org.everrest.websockets.message.ChannelBroadcastMessage;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import javax.websocket.EncodeException;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* Designed to broadcast high level events over web sockets to a client-side listener.
|
||||
* WS listener must beforehand know corresponding WE channel name and event DTO class,
|
||||
* because basically all this class does is broadcasting a DTO to a specific channel.
|
||||
* Nothing more.
|
||||
* <p>
|
||||
* Note: Proper DTO object and web socket channel name are assumed to be stored in
|
||||
* {@link HiEvent} instance passed to this broadcaster. Relevance of DTO and ws
|
||||
* chanel data is the responsibility of {@link HiEvent} instance creator.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class HiEventClientBroadcaster implements HiEventBroadcaster {
|
||||
private static final Logger LOG = getLogger(HiEventClientBroadcaster.class);
|
||||
|
||||
@Override
|
||||
public void broadcast(HiEvent event) {
|
||||
final ChannelBroadcastMessage bm = new ChannelBroadcastMessage();
|
||||
bm.setChannel(event.getChannel());
|
||||
bm.setBody(DtoFactory.getInstance().toJson(event.getDto()));
|
||||
|
||||
try {
|
||||
WSConnectionContext.sendMessage(bm);
|
||||
LOG.debug("Sending message over websocket connection: {}", bm);
|
||||
} catch (EncodeException | IOException e) {
|
||||
LOG.error("Can't send a VFS notification over web socket. Event: {}", event, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Event detectors are the key components of the whole VFS event handling subsystem.
|
||||
* <p>
|
||||
* The main purpose of each event detector is to analyze instances of
|
||||
* {@link EventTreeNode} and to find out if a specific conditions are met.
|
||||
* If yes - corresponding high level event should be generated. Besides the
|
||||
* obvious (event detection), event detectors are also responsible for high
|
||||
* level event generation. Those events should correspond to a specific event
|
||||
* tree state and populated with relevant data.
|
||||
* </p>
|
||||
* <p>
|
||||
* Perhaps those functions will be shared between detectors and event builders
|
||||
* in future implementations.
|
||||
* </p>
|
||||
* <p>
|
||||
* To omit details, it defines a way a set of low level events transforms into a
|
||||
* specific high level event or a list of events.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: Though it is not restricted to generate different high level events in
|
||||
* correspondence to different event trees by a single detector, it is not
|
||||
* recommended to do so to avoid complications in understanding and handling of
|
||||
* events. Adhere to a rule: one detector - one event. Unless you know what you
|
||||
* are doing.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@FunctionalInterface
|
||||
public interface HiEventDetector<T> {
|
||||
Optional<HiEvent<T>> detect(EventTreeNode eventTreeNode);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
class HiEventDetectorManager {
|
||||
private final Set<HiEventDetector<?>> hiEventDetectors;
|
||||
|
||||
@Inject
|
||||
public HiEventDetectorManager(Set<HiEventDetector<?>> hiEventDetectors) {
|
||||
this.hiEventDetectors = hiEventDetectors;
|
||||
}
|
||||
|
||||
List<HiEvent> getDetectedEvents(EventTreeNode root) {
|
||||
return hiEventDetectors.stream()
|
||||
.map(o -> o.detect(root))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* Event server-side publisher with a very basic set of functionality.
|
||||
* It simply publishes the events DTO to an instance of {@link EventService}
|
||||
* <p>
|
||||
* Note: DTO object is assumed to be stored in {@link HiEvent} instance
|
||||
* passed to this broadcaster.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class HiEventServerPublisher implements HiEventBroadcaster {
|
||||
private static final Logger LOG = getLogger(HiEventClientBroadcaster.class);
|
||||
|
||||
private final EventService eventService;
|
||||
|
||||
@Inject
|
||||
public HiEventServerPublisher(EventService eventService) {
|
||||
this.eventService = eventService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcast(HiEvent event) {
|
||||
eventService.publish(event.getDto());
|
||||
LOG.debug("Publishing event to event service: {}.", event.getDto());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* The main mission of this service is to manage event trees produced by {@link LoEventService}.
|
||||
* It is done with the help of {@link HiEventDetectorManager} and {@link HiEventBroadcasterManager}.
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class HiEventService extends VfsEventService {
|
||||
private final HiEventDetectorManager hiEventDetectorManager;
|
||||
private final HiEventBroadcasterManager hiEventBroadcasterManager;
|
||||
private final EventTreeQueueHolder eventTreeQueueHolder;
|
||||
|
||||
@Inject
|
||||
public HiEventService(HiEventDetectorManager hiEventDetectorManager,
|
||||
EventTreeQueueHolder eventTreeQueueHolder,
|
||||
HiEventBroadcasterManager hiEventBroadcasterManager) {
|
||||
this.hiEventDetectorManager = hiEventDetectorManager;
|
||||
this.eventTreeQueueHolder = eventTreeQueueHolder;
|
||||
this.hiEventBroadcasterManager = hiEventBroadcasterManager;
|
||||
}
|
||||
|
||||
protected void run() {
|
||||
final Optional<EventTreeNode> optional = eventTreeQueueHolder.take();
|
||||
if (optional.isPresent()) {
|
||||
final EventTreeNode treeRoot = optional.get();
|
||||
final List<HiEvent> events = hiEventDetectorManager.getDetectedEvents(treeRoot);
|
||||
hiEventBroadcasterManager.manageEvents(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
|
||||
import org.eclipse.che.api.vfs.VirtualFileSystem;
|
||||
import org.eclipse.che.api.vfs.impl.file.FileTreeWatcher;
|
||||
import org.eclipse.che.api.vfs.impl.file.FileWatcherNotificationListener;
|
||||
|
||||
/**
|
||||
* Low level virtual file system event implementation designed to represent
|
||||
* trivial file system items manipulations ({@link FileWatcherEventType}).
|
||||
* Roughly speaking it stands for physical file system operations (create file,
|
||||
* edit file, create folder, etc.).
|
||||
*
|
||||
* * <p>
|
||||
* It is used for both file and folder related events ({@link ItemType}) and
|
||||
* contains all relevant data (time, path, event and item types).
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: {@link LoEvent#getTime()} and {@link LoEvent#withTime(long)} methods
|
||||
* are designed to deal with the time that corresponds to the moment we caught
|
||||
* the event inside the application, not the real item modification time that
|
||||
* comes from underlying file system. This is due to the fact that most file
|
||||
* systems support timestamps accuracy within one second which is not enough
|
||||
* for our purposes.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
public class LoEvent {
|
||||
|
||||
/**
|
||||
* Time in milliseconds to represent the moment VFS event is fired by
|
||||
* {@link FileTreeWatcher} and caught by one of {@link FileWatcherNotificationListener}
|
||||
* implementations
|
||||
*/
|
||||
private long time;
|
||||
/**
|
||||
* Absolute item path in terms of {@link VirtualFileSystem}. It is expected
|
||||
* that this path starts from root position of file system. In common sense
|
||||
* it is the folder obtained by {@link VirtualFileSystem#getRoot()} method.
|
||||
*/
|
||||
private String path;
|
||||
private String name;
|
||||
private FileWatcherEventType eventType;
|
||||
private ItemType itemType;
|
||||
|
||||
private LoEvent() {
|
||||
}
|
||||
|
||||
public static LoEvent newInstance() {
|
||||
return new LoEvent();
|
||||
}
|
||||
|
||||
public ItemType getItemType() {
|
||||
return itemType;
|
||||
}
|
||||
|
||||
public LoEvent withItemType(ItemType itemType) {
|
||||
this.itemType = itemType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public LoEvent withTime(long time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public LoEvent withPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public LoEvent withName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileWatcherEventType getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public LoEvent withEventType(FileWatcherEventType eventType) {
|
||||
this.eventType = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum ItemType {
|
||||
FILE, DIR, UNDEFINED
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.eclipse.che.api.core.notification.EventSubscriber;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class LoEventListener implements EventSubscriber<LoEvent> {
|
||||
private static final Logger LOG = getLogger(LoEventListener.class);
|
||||
|
||||
private final EventService eventService;
|
||||
private final LoEventQueueHolder loEventQueueHolder;
|
||||
|
||||
@Inject
|
||||
public LoEventListener(EventService eventService, LoEventQueueHolder loEventQueueHolder) {
|
||||
this.eventService = eventService;
|
||||
this.loEventQueueHolder = loEventQueueHolder;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void postConstruct() {
|
||||
eventService.subscribe(this);
|
||||
LOG.info("Subscribing to event service: {}", this.getClass());
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void preDestroy() {
|
||||
eventService.unsubscribe(this);
|
||||
LOG.info("Unsubscribing to event service: {}", this.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(LoEvent event) {
|
||||
loEventQueueHolder.put(event);
|
||||
LOG.trace("Putting event {} to {}", event, LoEventQueueHolder.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
class LoEventQueueHolder {
|
||||
private static final Logger LOG = getLogger(LoEventQueueHolder.class);
|
||||
|
||||
private final BlockingQueue<LoEvent> loEventQueue;
|
||||
|
||||
public LoEventQueueHolder() {
|
||||
this.loEventQueue = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
void put(LoEvent loEvent) {
|
||||
try {
|
||||
loEventQueue.put(loEvent);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Error trying to put an event to an event queue: {}", loEvent, e);
|
||||
}
|
||||
}
|
||||
|
||||
Optional<LoEvent> poll(long timeout) {
|
||||
try {
|
||||
return Optional.ofNullable(loEventQueue.poll(timeout, MILLISECONDS));
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Error trying to poll an event out of an event queue", e);
|
||||
}
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeHelper.addEventAndCreatePrecedingNodes;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeNode.newRootInstance;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* Main task of event low event service is to take low level events and process
|
||||
* them into event tree according to their locations in a file system. The event
|
||||
* tree is passed further to a event detectors and broadcasters managed by upper
|
||||
* {@link HiEventService}.
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class LoEventService extends VfsEventService {
|
||||
private static final Logger LOG = getLogger(LoEventService.class);
|
||||
|
||||
/**
|
||||
* Maximal time interval between events that we consider them
|
||||
* to belong a common event segment.
|
||||
*/
|
||||
static final long MAX_EVENT_INTERVAL_MILLIS = 300L;
|
||||
/**
|
||||
* Maximal time interval that an event segment can last. This is used
|
||||
* to avoid long going events sequences. We are splitting them into several
|
||||
* lesser.
|
||||
*/
|
||||
static final long MAX_TIME_SEGMENT_SIZE_MILLIS = 5 * MAX_EVENT_INTERVAL_MILLIS;
|
||||
|
||||
/**
|
||||
* This constant is used to set undefined timestamp in case if a new event
|
||||
* segment is not started but the event interval is big enough to finish
|
||||
* previous event segment and to enqueue an old tree.
|
||||
*/
|
||||
private static final long UNDEFINED = -1L;
|
||||
|
||||
private final LoEventQueueHolder loEventQueueHolder;
|
||||
private final EventTreeQueueHolder eventTreeQueueHolder;
|
||||
|
||||
private EventTreeNode vfsEventTreeRoot;
|
||||
private long eventSegmentStartTime;
|
||||
|
||||
@Inject
|
||||
public LoEventService(LoEventQueueHolder loEventQueueHolder,
|
||||
EventTreeQueueHolder eventTreeQueueHolder) {
|
||||
this.loEventQueueHolder = loEventQueueHolder;
|
||||
this.eventTreeQueueHolder = eventTreeQueueHolder;
|
||||
|
||||
this.vfsEventTreeRoot = newRootInstance();
|
||||
this.eventSegmentStartTime = UNDEFINED;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
Optional<LoEvent> optional = loEventQueueHolder.poll(MAX_EVENT_INTERVAL_MILLIS);
|
||||
|
||||
if (optional.isPresent()) {
|
||||
final LoEvent loEvent = optional.get();
|
||||
final long eventTime = loEvent.getTime();
|
||||
|
||||
if (eventSegmentStartTime == UNDEFINED || eventTime - eventSegmentStartTime >= MAX_TIME_SEGMENT_SIZE_MILLIS) {
|
||||
LOG.trace("Starting new event segment.");
|
||||
LOG.trace("Old event segment start time: {} ", eventSegmentStartTime);
|
||||
LOG.trace("New event segment start time: {} ", eventTime);
|
||||
|
||||
flushOldTreeAndStartNew();
|
||||
eventSegmentStartTime = eventTime;
|
||||
}
|
||||
|
||||
addEventAndCreatePrecedingNodes(vfsEventTreeRoot, loEvent);
|
||||
} else {
|
||||
flushOldTreeAndStartNew();
|
||||
eventSegmentStartTime = UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
private void flushOldTreeAndStartNew() {
|
||||
if (vfsEventTreeRoot.getChildren().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventTreeQueueHolder.put(vfsEventTreeRoot);
|
||||
LOG.trace("Flushing old event tree {}.", vfsEventTreeRoot);
|
||||
|
||||
vfsEventTreeRoot = newRootInstance();
|
||||
LOG.trace("Starting new event tree {}.", vfsEventTreeRoot);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.PomModifiedEventDto;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.HiEvent.Category.PROJECT_INFRASTRUCTURE;
|
||||
import static org.eclipse.che.dto.server.DtoFactory.newDto;
|
||||
|
||||
/**
|
||||
* Detects if any of maven project object model files is MODIFIED during previous
|
||||
* event segment. If there are more than one modified file, generate event for the
|
||||
* highest (in context of file system location hierarchy) spotted.
|
||||
*
|
||||
* <p>
|
||||
* Note: this implementation deals only with standard project object model file
|
||||
* names - {@code pom.xml}. So if it is necessary to support custom naming you
|
||||
* can extend this class.
|
||||
* </p>
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
public class PomModifiedHiEventDetector implements HiEventDetector<PomModifiedEventDto> {
|
||||
|
||||
private static final String POM_XML = "pom.xml";
|
||||
|
||||
private HiEventBroadcaster broadcaster;
|
||||
|
||||
@Inject
|
||||
public PomModifiedHiEventDetector(HiEventServerPublisher broadcaster) {
|
||||
this.broadcaster = broadcaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<HiEvent<PomModifiedEventDto>> detect(EventTreeNode eventTreeNode) {
|
||||
if (!eventTreeNode.isRoot() || eventTreeNode.getChildren().isEmpty()) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
final Optional<EventTreeNode> highestPom = eventTreeNode.stream()
|
||||
.filter(EventTreeNode::modificationOccurred)
|
||||
.filter(EventTreeNode::isFile)
|
||||
.filter(event -> POM_XML.equals(event.getName()))
|
||||
.filter(event -> MODIFIED.equals(event.getLastEventType()))
|
||||
// note the revers order of o1 and o2
|
||||
.sorted((o1, o2) -> o2.getPath().compareTo(o1.getPath()))
|
||||
.findFirst();
|
||||
|
||||
if (!highestPom.isPresent()) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
PomModifiedEventDto dto = newDto(PomModifiedEventDto.class).withPath(highestPom.get().getPath());
|
||||
|
||||
return Optional.of((HiEvent.newInstance(PomModifiedEventDto.class)
|
||||
.withCategory(PROJECT_INFRASTRUCTURE.withPriority(50))
|
||||
.withBroadcaster(broadcaster)
|
||||
.withDto(dto)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@Beta
|
||||
abstract class VfsEventService {
|
||||
private static final Logger LOG = getLogger(VfsEventService.class);
|
||||
|
||||
private final ExecutorService executor;
|
||||
private final AtomicBoolean running;
|
||||
|
||||
VfsEventService() {
|
||||
final String threadName = getClass().getSimpleName().concat("Thread-%d");
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(threadName)
|
||||
.setDaemon(TRUE)
|
||||
.build();
|
||||
|
||||
this.executor = newSingleThreadExecutor(threadFactory);
|
||||
this.running = new AtomicBoolean(FALSE);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void postConstruct() {
|
||||
running.compareAndSet(FALSE, TRUE);
|
||||
executor.execute(this::runAsASingleThread);
|
||||
LOG.info("Starting virtual file system event service: {}", this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
protected void preDestroy() {
|
||||
running.compareAndSet(TRUE, FALSE);
|
||||
executor.shutdown();
|
||||
LOG.info("Stopping virtual file system event service: {}", this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
private void runAsASingleThread() {
|
||||
while (running.get()) {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void run();
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.CREATED;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.FILE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for {@link EventTreeNode}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
public class EventTreeNodeTest {
|
||||
|
||||
private static final String TEST_PATH = separator + "che" + separator + "file";
|
||||
private static final String CHILD_NAME_1 = "Child 1";
|
||||
private static final String CHILD_NAME_2 = "Child 2";
|
||||
|
||||
private EventTreeNode root;
|
||||
private EventTreeNode child;
|
||||
private EventTreeNode anotherChild;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
root = EventTreeNode.newRootInstance();
|
||||
child = EventTreeNode.newInstance();
|
||||
anotherChild = EventTreeNode.newInstance();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProperlyAddChild() {
|
||||
root.withChild(child);
|
||||
|
||||
final Optional<EventTreeNode> optional = root.getFirstChild();
|
||||
|
||||
assertTrue(optional.isPresent());
|
||||
assertEquals(optional.get(), child);
|
||||
assertEquals(1, root.getChildren().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProperlyAddEvent() {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
assertTrue(child.getEvents().isEmpty());
|
||||
|
||||
child.withEvent(getLoVfsEvent(time, CREATED));
|
||||
|
||||
assertEquals(1, child.getEvents().size());
|
||||
assertNotNull(child.getEvents().get(time));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProperlyGetFirstChild() {
|
||||
root.withChild(child.withName(CHILD_NAME_1));
|
||||
root.withChild(anotherChild.withName(CHILD_NAME_2));
|
||||
|
||||
assertTrue(root.getFirstChild().isPresent());
|
||||
assertEquals(CHILD_NAME_1, root.getFirstChild().get().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldProperlyGetLastEventType() {
|
||||
child.withEvent(getLoVfsEvent(System.currentTimeMillis(), CREATED));
|
||||
child.withEvent(getLoVfsEvent(System.currentTimeMillis(), MODIFIED));
|
||||
|
||||
assertEquals(MODIFIED, child.getLastEventType());
|
||||
}
|
||||
|
||||
private LoEvent getLoVfsEvent(long time, FileWatcherEventType type) {
|
||||
return LoEvent.newInstance()
|
||||
.withPath(TEST_PATH)
|
||||
.withTime(time)
|
||||
.withItemType(FILE)
|
||||
.withEventType(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.GitBranchCheckoutEventDto;
|
||||
import org.eclipse.che.api.vfs.VirtualFile;
|
||||
import org.eclipse.che.api.vfs.VirtualFileSystem;
|
||||
import org.eclipse.che.api.vfs.VirtualFileSystemProvider;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.CREATED;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.FILE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test for {@link GitCheckoutHiEventDetector}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class GitCheckoutHiEventDetectorTest extends HiVfsEventDetectorTestHelper {
|
||||
|
||||
private static final String PROJECT_NAME = "project";
|
||||
private static final String HEAD_FILE_PATH = ".git/HEAD";
|
||||
private static final String TEST_VALUE = "TEST VALUE";
|
||||
private static final String HEAD_FILE_CONTENT = "ref: refs" + File.separator + "heads" + File.separator + TEST_VALUE;
|
||||
private static final String HEAD_FILE_NAME = "HEAD";
|
||||
private static final String GIT_OPERATIONS_CHANNEL = "git-operations-channel";
|
||||
|
||||
@Mock
|
||||
private HiEventClientBroadcaster hiVfsEventClientBroadcaster;
|
||||
@Mock
|
||||
private VirtualFileSystemProvider virtualFileSystemProvider;
|
||||
|
||||
private GitCheckoutHiEventDetector gitCheckoutHiVfsEventDetector;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
gitCheckoutHiVfsEventDetector = new GitCheckoutHiEventDetector(virtualFileSystemProvider, hiVfsEventClientBroadcaster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnEmptyListBecauseNoHeadFileModified() {
|
||||
assertFalse(gitCheckoutHiVfsEventDetector.detect(root).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnEmptyListBecauseOfWrongHeadFileModification() {
|
||||
addEvent(HEAD_FILE_NAME, File.separator + PROJECT_NAME + File.separator + HEAD_FILE_PATH, CREATED, FILE);
|
||||
|
||||
assertFalse(gitCheckoutHiVfsEventDetector.detect(root).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnListWithCorrectEventBecauseHeadFileIsProperlyModified() throws Exception {
|
||||
VirtualFileSystem vfs = mock(VirtualFileSystem.class);
|
||||
VirtualFile file = mock(VirtualFile.class);
|
||||
|
||||
when(virtualFileSystemProvider.getVirtualFileSystem()).thenReturn(vfs);
|
||||
when(vfs.getRoot()).thenReturn(file);
|
||||
when(file.getChild(any())).thenReturn(file);
|
||||
when(file.getContentAsString()).thenReturn(HEAD_FILE_CONTENT);
|
||||
|
||||
addEvent(HEAD_FILE_NAME, File.separator + PROJECT_NAME + File.separator + HEAD_FILE_PATH, MODIFIED, FILE);
|
||||
|
||||
Optional<HiEvent<GitBranchCheckoutEventDto>> optional = gitCheckoutHiVfsEventDetector.detect(root);
|
||||
assertTrue(optional.isPresent());
|
||||
|
||||
HiEvent<GitBranchCheckoutEventDto> hiEvent = optional.get();
|
||||
assertEquals(GIT_OPERATIONS_CHANNEL, hiEvent.getChannel());
|
||||
assertEquals(TEST_VALUE, hiEvent.getDto().getBranchName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Test for {@link HiEventService}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HiEventServiceTest {
|
||||
@Mock
|
||||
private HiEventDetectorManager hiEventDetectorManager;
|
||||
@Mock
|
||||
private HiEventBroadcasterManager hiEventBroadcasterManager;
|
||||
@Spy
|
||||
private EventTreeQueueHolder eventTreeQueueHolder;
|
||||
@InjectMocks
|
||||
private HiEventService hiEventService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
hiEventService.postConstruct();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
hiEventService.preDestroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInvokeAllComponents() throws Exception {
|
||||
final EventTreeNode eventTreeNode = mock(EventTreeNode.class);
|
||||
|
||||
eventTreeQueueHolder.put(eventTreeNode);
|
||||
|
||||
sleep(1000);
|
||||
|
||||
verify(eventTreeQueueHolder, atLeastOnce()).take();
|
||||
verify(hiEventDetectorManager).getDetectedEvents(eq(eventTreeNode));
|
||||
verify(hiEventBroadcasterManager).manageEvents(any());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
|
||||
import org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType;
|
||||
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeHelper.addEventAndCreatePrecedingNodes;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeNode.newRootInstance;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.newInstance;
|
||||
|
||||
/**
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
abstract class HiVfsEventDetectorTestHelper {
|
||||
|
||||
protected EventTreeNode root;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
root = newRootInstance();
|
||||
}
|
||||
|
||||
void addEvent(String name,
|
||||
String path,
|
||||
FileWatcherEventType eventType,
|
||||
ItemType itemType) {
|
||||
|
||||
LoEvent loEvent = newInstance()
|
||||
.withName(name)
|
||||
.withPath(path)
|
||||
.withEventType(eventType)
|
||||
.withItemType(itemType)
|
||||
.withTime(System.currentTimeMillis());
|
||||
|
||||
addEventAndCreatePrecedingNodes(root, loEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
|
||||
import org.eclipse.che.api.core.notification.EventService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Test for {@link LoEventListener}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LoEventListenerTest {
|
||||
@Spy
|
||||
private EventService eventService;
|
||||
@Mock
|
||||
private LoEventQueueHolder loEventQueueHolder;
|
||||
@InjectMocks
|
||||
private LoEventListener loEventListener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
loEventListener.postConstruct();
|
||||
verify(eventService).subscribe(any(LoEventListener.class));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
loEventListener.preDestroy();
|
||||
verify(eventService).unsubscribe(isA(LoEventListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldEnqueuePublishedVfsEvent() throws Exception {
|
||||
final LoEvent loEvent = mock(LoEvent.class);
|
||||
|
||||
eventService.publish(loEvent);
|
||||
verify(loEventQueueHolder).put(eq(loEvent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotEnqueuePublishedNonVfsEvent() throws Exception {
|
||||
eventService.publish(new Object());
|
||||
verifyZeroInteractions(loEventQueueHolder);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.lang.Thread.sleep;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.FILE;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.newInstance;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEventService.MAX_EVENT_INTERVAL_MILLIS;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEventService.MAX_TIME_SEGMENT_SIZE_MILLIS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for {@link LoEventService}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LoEventServiceTest {
|
||||
private static final long TIMEOUT = 2 * MAX_EVENT_INTERVAL_MILLIS;
|
||||
private static final String FOLDER_NAME = "folder";
|
||||
private static final String FILE_NAME = "file";
|
||||
private static final String PATH = separator + FOLDER_NAME + separator + FILE_NAME;
|
||||
|
||||
@Spy
|
||||
private LoEventQueueHolder loEventQueueHolder;
|
||||
@Spy
|
||||
private EventTreeQueueHolder eventTreeQueueHolder;
|
||||
@InjectMocks
|
||||
private LoEventService loEventService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
loEventService.postConstruct();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
loEventService.preDestroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConsumeSimpleEventSegmentAndProduceEventTree() throws Exception {
|
||||
final LoEvent loEvent = getLoEvent(PATH, FILE_NAME);
|
||||
loEventQueueHolder.put(loEvent);
|
||||
final Optional<EventTreeNode> rootOptional = eventTreeQueueHolder.take();
|
||||
|
||||
assertNotNull(rootOptional);
|
||||
assertTrue(rootOptional.isPresent());
|
||||
|
||||
final EventTreeNode root = rootOptional.get();
|
||||
assertTrue(root.isRoot());
|
||||
assertFalse(root.modificationOccurred());
|
||||
|
||||
final Optional<EventTreeNode> dirOptional = root.getChild(FOLDER_NAME);
|
||||
assertNotNull(dirOptional);
|
||||
assertTrue(dirOptional.isPresent());
|
||||
|
||||
final EventTreeNode dir = dirOptional.get();
|
||||
assertEquals(1, dir.getChildren().size());
|
||||
assertFalse(dir.modificationOccurred());
|
||||
|
||||
final Optional<EventTreeNode> fileOptional = dir.getChild(FILE_NAME);
|
||||
|
||||
assertNotNull(fileOptional);
|
||||
assertTrue(fileOptional.isPresent());
|
||||
|
||||
final EventTreeNode file = fileOptional.get();
|
||||
assertTrue(file.modificationOccurred());
|
||||
assertEquals(MODIFIED, file.getLastEventType());
|
||||
assertTrue(file.isFile());
|
||||
assertEquals(PATH, file.getPath());
|
||||
assertEquals(FILE_NAME, file.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConsumeTwoEventSegmentsAndProduceTwoEventTries() throws Exception {
|
||||
final LoEvent loEventI = getLoEvent(separator + FOLDER_NAME + 1 + separator + FILE_NAME + 1, FILE_NAME + 1);
|
||||
loEventQueueHolder.put(loEventI);
|
||||
final Optional<EventTreeNode> rootOptionalI = eventTreeQueueHolder.take();
|
||||
|
||||
sleep(TIMEOUT); // this is required to properly simulate delay between events, should exceed max event interval
|
||||
|
||||
final LoEvent loEventII = getLoEvent(separator + FOLDER_NAME + 2 + separator + FILE_NAME + 2, FILE_NAME + 2);
|
||||
loEventQueueHolder.put(loEventII);
|
||||
final Optional<EventTreeNode> rootOptionalII = eventTreeQueueHolder.take();
|
||||
|
||||
assertNotNull(rootOptionalI);
|
||||
assertTrue(rootOptionalI.isPresent());
|
||||
assertNotNull(rootOptionalII);
|
||||
assertTrue(rootOptionalII.isPresent());
|
||||
|
||||
final EventTreeNode rootI = rootOptionalI.get();
|
||||
final EventTreeNode rootII = rootOptionalII.get();
|
||||
|
||||
assertEquals(1, rootI.getChildren().size());
|
||||
assertEquals(1, rootII.getChildren().size());
|
||||
|
||||
final Optional<EventTreeNode> folderOptionalI = rootI.getChild(FOLDER_NAME + 1);
|
||||
final Optional<EventTreeNode> folderOptionalII = rootII.getChild(FOLDER_NAME + 2);
|
||||
|
||||
assertNotNull(folderOptionalI);
|
||||
assertTrue(folderOptionalI.isPresent());
|
||||
assertNotNull(folderOptionalI);
|
||||
assertTrue(folderOptionalII.isPresent());
|
||||
|
||||
final EventTreeNode folderI = folderOptionalI.get();
|
||||
final EventTreeNode folderII = folderOptionalII.get();
|
||||
|
||||
assertEquals(1, folderI.getChildren().size());
|
||||
assertEquals(1, folderII.getChildren().size());
|
||||
|
||||
final Optional<EventTreeNode> fileOptionalI = folderI.getChild(FILE_NAME + 1);
|
||||
final Optional<EventTreeNode> fileOptionalII = folderII.getChild(FILE_NAME + 2);
|
||||
|
||||
assertNotNull(fileOptionalI);
|
||||
assertTrue(fileOptionalI.isPresent());
|
||||
assertNotNull(fileOptionalII);
|
||||
assertTrue(fileOptionalII.isPresent());
|
||||
|
||||
final EventTreeNode fileI = fileOptionalI.get();
|
||||
final EventTreeNode fileII = fileOptionalII.get();
|
||||
|
||||
assertEquals(FILE_NAME + 1, fileI.getName());
|
||||
assertEquals(FILE_NAME + 2, fileII.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateEventTreeBecauseOfExceedingEventSegmentTimeFrame() throws Exception {
|
||||
final long sleepTime = 150;
|
||||
final long start = currentTimeMillis();
|
||||
|
||||
long totalCounter = 0;
|
||||
long firstSegmentCounter = 0;
|
||||
|
||||
while ((currentTimeMillis() - start) < MAX_TIME_SEGMENT_SIZE_MILLIS + MAX_EVENT_INTERVAL_MILLIS) {
|
||||
if ((firstSegmentCounter == 0) && ((currentTimeMillis() - start) > MAX_TIME_SEGMENT_SIZE_MILLIS)) {
|
||||
firstSegmentCounter = totalCounter;
|
||||
}
|
||||
totalCounter++;
|
||||
loEventQueueHolder.put(getLoEvent(PATH + totalCounter, FILE_NAME + totalCounter));
|
||||
sleep(sleepTime);
|
||||
}
|
||||
|
||||
final Optional<EventTreeNode> rootOptionalI = eventTreeQueueHolder.take();
|
||||
final Optional<EventTreeNode> rootOptionalII = eventTreeQueueHolder.take();
|
||||
assertTrue(rootOptionalI.isPresent());
|
||||
assertTrue(rootOptionalII.isPresent());
|
||||
|
||||
final Optional<EventTreeNode> folderI = rootOptionalI.get().getFirstChild();
|
||||
final Optional<EventTreeNode> folderII = rootOptionalII.get().getFirstChild();
|
||||
assertTrue(folderI.isPresent());
|
||||
assertTrue(folderII.isPresent());
|
||||
|
||||
assertEquals(firstSegmentCounter, folderI.get().getChildren().size());
|
||||
assertEquals(totalCounter - firstSegmentCounter, folderII.get().getChildren().size());
|
||||
}
|
||||
|
||||
private LoEvent getLoEvent(String path, String name) {
|
||||
return newInstance().withName(name)
|
||||
.withPath(path)
|
||||
.withEventType(MODIFIED)
|
||||
.withItemType(FILE)
|
||||
.withTime(currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeHelper.addEventAndCreatePrecedingNodes;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeHelper.getTreeNode;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.EventTreeNode.newRootInstance;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.newInstance;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for {@link EventTreeHelper}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
public class LoEventTreeHelperTest {
|
||||
private static final String CHE = "che";
|
||||
private static final String CHU = "chu";
|
||||
private static final String CHA = "cha";
|
||||
private static final String CHI = "chi";
|
||||
private static final String PATH = separator + CHE + separator + CHU + separator + CHA + separator + CHI;
|
||||
|
||||
private EventTreeNode root;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
root = newRootInstance();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldImplicitlyCreateAllAncestors() {
|
||||
final LoEvent loEvent = newInstance().withPath(PATH);
|
||||
addEventAndCreatePrecedingNodes(root, loEvent);
|
||||
testNode(testNode(testNode(testNode(root, CHE), CHU), CHA), CHI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetExistingTreeNode() {
|
||||
final LoEvent loEvent = newInstance().withName(CHI).withPath(PATH);
|
||||
|
||||
addEventAndCreatePrecedingNodes(root, loEvent);
|
||||
|
||||
final Optional<EventTreeNode> nodeOptional = getTreeNode(root, PATH);
|
||||
assertTrue(nodeOptional.isPresent());
|
||||
|
||||
final EventTreeNode node = nodeOptional.get();
|
||||
assertEquals(loEvent.getPath(), node.getPath());
|
||||
assertEquals(loEvent.getName(), node.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotGetNonExistingTreeNode() {
|
||||
assertFalse(getTreeNode(root, CHE).isPresent());
|
||||
}
|
||||
|
||||
private EventTreeNode testNode(EventTreeNode parent, String name) {
|
||||
final Optional<EventTreeNode> nodeOptional = parent.getFirstChild();
|
||||
assertTrue(nodeOptional.isPresent());
|
||||
|
||||
final EventTreeNode node = nodeOptional.get();
|
||||
assertNotNull(node);
|
||||
assertEquals(name, node.getName());
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*******************************************************************************
|
||||
* 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.api.vfs.impl.file.event;
|
||||
|
||||
import org.eclipse.che.api.project.shared.dto.event.PomModifiedEventDto;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.io.File.separator;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.CREATED;
|
||||
import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.HiEvent.Category.PROJECT_INFRASTRUCTURE;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.DIR;
|
||||
import static org.eclipse.che.api.vfs.impl.file.event.LoEvent.ItemType.FILE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for {@link PomModifiedHiEventDetector}
|
||||
*
|
||||
* @author Dmitry Kuleshov
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PomModifiedHiEventDetectorTest extends HiVfsEventDetectorTestHelper {
|
||||
|
||||
private static final String CHE_PATH = separator + "che" + separator;
|
||||
private static final String FOLDER_1 = CHE_PATH + "folder1" + separator;
|
||||
private static final String PATH_2 = CHE_PATH + "folder2" + separator;
|
||||
private static final String TEST = "test";
|
||||
private static final String POM_XML = "pom.xml";
|
||||
@Mock
|
||||
private HiEventServerPublisher hiVfsEventServerPublisher;
|
||||
|
||||
private PomModifiedHiEventDetector pomModifiedHiVfsEventDetector;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
pomModifiedHiVfsEventDetector = new PomModifiedHiEventDetector(hiVfsEventServerPublisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnEmptyListBecauseNoPomFileFound() {
|
||||
assertFalse(pomModifiedHiVfsEventDetector.detect(root).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnEmptyListBecauseNoRelevantPomFileFound() {
|
||||
addEvent(POM_XML, FOLDER_1 + POM_XML, CREATED, FILE);
|
||||
addEvent(POM_XML, PATH_2 + POM_XML, MODIFIED, DIR);
|
||||
addEvent(TEST, CHE_PATH + POM_XML + separator + TEST, MODIFIED, FILE);
|
||||
|
||||
assertFalse(pomModifiedHiVfsEventDetector.detect(root).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnListWithPomEvent() {
|
||||
addEvent(POM_XML, FOLDER_1 + POM_XML, CREATED, FILE);
|
||||
addEvent(POM_XML, FOLDER_1 + POM_XML, MODIFIED, FILE);
|
||||
addEvent(POM_XML, PATH_2 + POM_XML, MODIFIED, DIR);
|
||||
addEvent(TEST, CHE_PATH + POM_XML + separator + TEST, MODIFIED, FILE);
|
||||
|
||||
final Optional<HiEvent<PomModifiedEventDto>> eventOptional = pomModifiedHiVfsEventDetector.detect(root);
|
||||
assertTrue(eventOptional.isPresent());
|
||||
|
||||
final HiEvent<PomModifiedEventDto> hiEvent = eventOptional.get();
|
||||
assertEquals(PROJECT_INFRASTRUCTURE, hiEvent.getCategory());
|
||||
assertEquals(FOLDER_1 + POM_XML, hiEvent.getDto().getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnListWithPomWithHighestFsHierarchyPosition() {
|
||||
addEvent(POM_XML, FOLDER_1 + POM_XML, MODIFIED, FILE);
|
||||
addEvent(POM_XML, CHE_PATH + POM_XML, MODIFIED, FILE);
|
||||
|
||||
final Optional<HiEvent<PomModifiedEventDto>> eventOptional = pomModifiedHiVfsEventDetector.detect(root);
|
||||
assertTrue(eventOptional.isPresent());
|
||||
|
||||
final HiEvent<PomModifiedEventDto> hiEvent = eventOptional.get();
|
||||
assertEquals(CHE_PATH + POM_XML, hiEvent.getDto().getPath());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue