blob: 2cc4b2eb5cca2b7a5314a40b33eb83564b44efe9 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.externalSystem.service;
import com.intellij.CommonBundle;
import com.intellij.debugger.ui.DebuggerView;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.CommandLineState;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.SimpleJavaParameters;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessTerminatedListener;
import com.intellij.execution.rmi.RemoteProcessSupport;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.ide.actions.OpenProjectFileChooserDescriptor;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager;
import com.intellij.openapi.externalSystem.service.remote.ExternalSystemProgressNotificationManagerImpl;
import com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProgressNotificationManager;
import com.intellij.openapi.externalSystem.service.remote.wrapper.ExternalSystemFacadeWrapper;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.JdkUtil;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SimpleJavaSdkType;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.psi.PsiBundle;
import com.intellij.ui.PlaceHolder;
import com.intellij.util.Alarm;
import com.intellij.util.PathUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Denis Zhdanov
* @since 8/9/13 3:37 PM
*/
public class RemoteExternalSystemCommunicationManager implements ExternalSystemCommunicationManager {
private static final Logger LOG = Logger.getInstance("#" + RemoteExternalSystemCommunicationManager.class.getName());
private static final String MAIN_CLASS_NAME = RemoteExternalSystemFacadeImpl.class.getName();
private final AtomicReference<RemoteExternalSystemProgressNotificationManager> myExportedNotificationManager
= new AtomicReference<RemoteExternalSystemProgressNotificationManager>();
@NotNull private final ThreadLocal<ProjectSystemId> myTargetExternalSystemId = new ThreadLocal<ProjectSystemId>();
@NotNull private final ExternalSystemProgressNotificationManagerImpl myProgressManager;
@NotNull private final RemoteProcessSupport<Object, RemoteExternalSystemFacade, String> mySupport;
public RemoteExternalSystemCommunicationManager(@NotNull ExternalSystemProgressNotificationManager notificationManager) {
myProgressManager = (ExternalSystemProgressNotificationManagerImpl)notificationManager;
mySupport = new RemoteProcessSupport<Object, RemoteExternalSystemFacade, String>(RemoteExternalSystemFacade.class) {
@Override
protected void fireModificationCountChanged() {
}
@Override
protected String getName(Object o) {
return RemoteExternalSystemFacade.class.getName();
}
@Override
protected RunProfileState getRunProfileState(Object o, String configuration, Executor executor) throws ExecutionException {
return createRunProfileState(configuration);
}
};
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
public void run() {
shutdown(false);
}
});
}
public synchronized void shutdown(boolean wait) {
mySupport.stopAll(wait);
}
private RunProfileState createRunProfileState(final String configuration) {
return new CommandLineState(null) {
private SimpleJavaParameters createJavaParameters() throws ExecutionException {
final SimpleJavaParameters params = new SimpleJavaParameters();
params.setJdk(new SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome()));
File myWorkingDirectory = new File(configuration);
params.setWorkingDirectory(myWorkingDirectory.isDirectory() ? myWorkingDirectory.getPath() : PathManager.getBinPath());
final List<String> classPath = ContainerUtilRt.newArrayList();
// IDE jars.
classPath.addAll(PathManager.getUtilClassPath());
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(ProjectBundle.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(PlaceHolder.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(DebuggerView.class), classPath);
ExternalSystemApiUtil.addBundle(params.getClassPath(), "messages.ProjectBundle", ProjectBundle.class);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(PsiBundle.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(Alarm.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(DependencyScope.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(ExtensionPointName.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(OpenProjectFileChooserDescriptor.class), classPath);
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(ExternalSystemTaskNotificationListener.class), classPath);
// External system module jars
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(getClass()), classPath);
ExternalSystemApiUtil.addBundle(params.getClassPath(), "messages.CommonBundle", CommonBundle.class);
params.getClassPath().addAll(classPath);
params.setMainClass(MAIN_CLASS_NAME);
params.getVMParametersList().addParametersString("-Djava.awt.headless=true");
// It may take a while for gradle api to resolve external dependencies. Default RMI timeout
// is 15 seconds (http://download.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html#connectionTimeout),
// we don't want to get EOFException because of that.
params.getVMParametersList().addParametersString(
"-Dsun.rmi.transport.connectionTimeout=" + String.valueOf(TimeUnit.HOURS.toMillis(1))
);
// params.getVMParametersList().addParametersString("-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5009");
ProjectSystemId externalSystemId = myTargetExternalSystemId.get();
if (externalSystemId != null) {
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
if (manager != null) {
params.getClassPath().add(PathUtil.getJarPathForClass(manager.getProjectResolverClass().getClass()));
params.getProgramParametersList().add(manager.getProjectResolverClass().getName());
params.getProgramParametersList().add(manager.getTaskManagerClass().getName());
manager.enhanceRemoteProcessing(params);
}
}
return params;
}
@Override
@NotNull
public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
ProcessHandler processHandler = startProcess();
return new DefaultExecutionResult(null, processHandler, AnAction.EMPTY_ARRAY);
}
@NotNull
protected OSProcessHandler startProcess() throws ExecutionException {
SimpleJavaParameters params = createJavaParameters();
Sdk sdk = params.getJdk();
if (sdk == null) {
throw new ExecutionException("No sdk is defined. Params: " + params);
}
final GeneralCommandLine commandLine = JdkUtil.setupJVMCommandLine(
((JavaSdkType)sdk.getSdkType()).getVMExecutablePath(sdk),
params,
false
);
final OSProcessHandler processHandler = new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString()) {
@Override
public Charset getCharset() {
return commandLine.getCharset();
}
};
ProcessTerminatedListener.attach(processHandler);
return processHandler;
}
};
}
@Nullable
@Override
public RemoteExternalSystemFacade acquire(@NotNull String id, @NotNull ProjectSystemId externalSystemId)
throws Exception
{
myTargetExternalSystemId.set(externalSystemId);
final RemoteExternalSystemFacade facade;
try {
facade = mySupport.acquire(this, id);
}
finally {
myTargetExternalSystemId.set(null);
}
if (facade == null) {
return null;
}
RemoteExternalSystemProgressNotificationManager exported = myExportedNotificationManager.get();
if (exported == null) {
try {
exported = (RemoteExternalSystemProgressNotificationManager)UnicastRemoteObject.exportObject(myProgressManager, 0);
myExportedNotificationManager.set(exported);
}
catch (RemoteException e) {
exported = myExportedNotificationManager.get();
}
}
if (exported == null) {
LOG.warn("Can't export progress manager");
}
else {
facade.applyProgressManager(exported);
}
return facade;
}
@Override
public void release(@NotNull String id, @NotNull ProjectSystemId externalSystemId) throws Exception {
mySupport.release(this, id);
}
@Override
public boolean isAlive(@NotNull RemoteExternalSystemFacade facade) {
RemoteExternalSystemFacade toCheck = facade;
if (facade instanceof ExternalSystemFacadeWrapper) {
toCheck = ((ExternalSystemFacadeWrapper)facade).getDelegate();
}
if (toCheck instanceof InProcessExternalSystemFacadeImpl) {
return false;
}
try {
facade.getResolver();
return true;
}
catch (RemoteException e) {
return false;
}
}
@Override
public void clear() {
mySupport.stopAll(true);
}
}