/*******************************************************************************
* Copyright (c) 2012-2015 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.jdt;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.notification.EventSubscriber;
import org.eclipse.che.api.vfs.server.observation.VirtualFileEvent;
import org.eclipse.che.commons.lang.IoUtil;
import org.eclipse.che.jdt.core.resources.ResourceChangedEvent;
import org.eclipse.che.jdt.internal.core.JavaProject;
import org.eclipse.che.vfs.impl.fs.LocalFSMountStrategy;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.codeassist.impl.AssistOptions;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
/**
* Maintenance and create JavaProjects
*
* @author Evgen Vidolob
*/
@Singleton
public class JavaProjectService {
/** Logger. */
private static final Logger LOG = LoggerFactory.getLogger(JavaProjectService.class);
private Cache<String, JavaProject> cache;
private ConcurrentHashMap<String, CopyOnWriteArraySet<String>> projectInWs = new ConcurrentHashMap<>();
private LocalFSMountStrategy fsMountStrategy;
private String tempDir;
private Map<String, String> options = new HashMap<>();
@Inject
public JavaProjectService(EventService eventService,
LocalFSMountStrategy fsMountStrategy,
@Named("che.java.codeassistant.index.dir") String temp) {
eventService.subscribe(new VirtualFileEventSubscriber());
this.fsMountStrategy = fsMountStrategy;
tempDir = temp;
options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7);
options.put(JavaCore.CORE_ENCODING, "UTF-8");
options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7);
options.put(CompilerOptions.OPTION_TargetPlatform, JavaCore.VERSION_1_7);
options.put(AssistOptions.OPTION_PerformVisibilityCheck, AssistOptions.ENABLED);
options.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING);
options.put(CompilerOptions.OPTION_TaskTags, CompilerOptions.WARNING);
options.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.WARNING);
options.put(CompilerOptions.OPTION_SuppressWarnings, CompilerOptions.DISABLED);
options.put(JavaCore.COMPILER_TASK_TAGS, "TODO,FIXME,XXX");
options.put(JavaCore.COMPILER_PB_UNUSED_PARAMETER_INCLUDE_DOC_COMMENT_REFERENCE, JavaCore.ENABLED);
options.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED);
options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
options.put(CompilerOptions.OPTION_Process_Annotations, JavaCore.ENABLED);
options.put(CompilerOptions.OPTION_GenerateClassFiles, JavaCore.ENABLED);
cache = CacheBuilder.newBuilder().expireAfterAccess(4, TimeUnit.HOURS).removalListener(
new RemovalListener<String, JavaProject>() {
@Override
public void onRemoval(RemovalNotification<String, JavaProject> notification) {
JavaProject value = notification.getValue();
if (value != null) {
removeProject(value.getWsId(), value.getProjectPath());
}
}
}).build();
}
public JavaProject getOrCreateJavaProject(String wsId, String projectPath) {
String key = wsId + projectPath;
JavaProject project = cache.getIfPresent(key);
if (project != null) {
return project;
}
File mountPath;
try {
mountPath = fsMountStrategy.getMountPath(wsId);
} catch (ServerException e) {
throw new RuntimeException(e);
}
JavaProject javaProject = new JavaProject(mountPath, projectPath, tempDir, wsId, new HashMap<>(options));
cache.put(key, javaProject);
if (!projectInWs.containsKey(wsId)) {
projectInWs.put(wsId, new CopyOnWriteArraySet<String>());
}
projectInWs.get(wsId).add(projectPath);
return javaProject;
}
public boolean isProjectDependencyExist(String wsId, String projectPath) {
if (cache.asMap().containsKey(wsId + projectPath)) {
return true;
}
File projectDepDir = new File(tempDir, wsId + projectPath);
return projectDepDir.exists();
}
public void removeProject(String wsId, String projectPath) {
JavaProject javaProject = cache.getIfPresent(wsId + projectPath);
if (projectInWs.containsKey(wsId)) {
projectInWs.get(wsId).remove(projectPath);
}
if (javaProject != null) {
cache.invalidate(wsId + projectPath);
try {
javaProject.close();
} catch (JavaModelException e) {
LOG.error("Error when trying close project.", e);
}
}
deleteDependencyDirectory(wsId, projectPath);
}
public Map<String, String> getOptions() {
return options;
}
private void deleteDependencyDirectory(String wsId, String projectPath) {
File projectDepDir = new File(tempDir, wsId + projectPath);
if (projectDepDir.exists()) {
IoUtil.deleteRecursive(projectDepDir);
File wsDepDir = new File(tempDir, wsId);
if (wsDepDir.exists()) {
String[] files = wsDepDir.list();
if (files == null || files.length == 0) {
wsDepDir.delete();
}
}
}
}
private class VirtualFileEventSubscriber implements EventSubscriber<VirtualFileEvent> {
@Override
public void onEvent(VirtualFileEvent event) {
final VirtualFileEvent.ChangeType eventType = event.getType();
final String eventWorkspace = event.getWorkspaceId();
final String eventPath = event.getPath();
try {
if (eventType == VirtualFileEvent.ChangeType.DELETED) {
JavaProject javaProject = cache.getIfPresent(eventWorkspace + eventPath);
if (javaProject != null) {
removeProject(eventWorkspace, eventPath);
return;
} else if (event.isFolder()) {
if (isProjectDependencyExist(eventWorkspace, eventPath)) {
deleteDependencyDirectory(eventWorkspace, eventPath);
return;
}
}
}
if (projectInWs.containsKey(eventWorkspace)) {
for (String path : projectInWs.get(eventWorkspace)) {
if (eventPath.startsWith(path)) {
JavaProject javaProject = cache.getIfPresent(eventWorkspace + path);
if (javaProject != null) {
try {
javaProject.getJavaModelManager().deltaState.resourceChanged(
new ResourceChangedEvent(fsMountStrategy.getMountPath(eventWorkspace), event));
javaProject.creteNewNameEnvironment();
} catch (ServerException e) {
LOG.error("Can't update java model", e);
}
}
break;
}
}
}
} catch (Throwable t) {
//catch all exceptions that may be happened
LOG.error("Can't update java model", t);
}
}
}
}