IDEA-111831 External system: Make it possible to communicate with external system at the ide process

This commit is contained in:
Denis.Zhdanov
2013-08-09 19:17:09 +04:00
parent db1fb06705
commit 00df0608a8
11 changed files with 708 additions and 362 deletions
@@ -33,6 +33,8 @@ public class ExternalSystemConstants {
@NonNls @NotNull public static final String TOOL_WINDOW_PLACE = "ExternalSystem.ToolWindow";
@NonNls @NotNull public static final String TREE_CONTEXT_MENU_PLACE = "ExternalSystem.Tree.Context.Menu";
@NotNull @NonNls public static final String USE_IN_PROCESS_COMMUNICATION_REGISTRY_KEY = "external.system.in.process";
@NotNull public static final String DEBUG_RUNNER_ID = "ExternalSystemTaskDebugRunner";
@NotNull public static final String RUNNER_ID = "ExternalSystemTaskRunner";
@@ -1,106 +1,66 @@
package com.intellij.openapi.externalSystem.service;
import com.intellij.execution.rmi.RemoteServer;
import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings;
import com.intellij.openapi.externalSystem.model.task.*;
import com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver;
import com.intellij.openapi.externalSystem.service.remote.*;
import com.intellij.util.Alarm;
import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
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.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Denis Zhdanov
* @since 8/8/11 12:51 PM
*/
public class ExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings> extends RemoteServer
public abstract class AbstractExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings> extends RemoteServer
implements RemoteExternalSystemFacade<S>
{
private static final long DEFAULT_REMOTE_GRADLE_PROCESS_TTL_IN_MS = TimeUnit.MILLISECONDS.convert(3, TimeUnit.MINUTES);
private final ConcurrentMap<Class<?>, RemoteExternalSystemService<S>> myRemotes = ContainerUtil.newConcurrentMap();
private final AtomicReference<S> mySettings = new AtomicReference<S>();
private final AtomicLong myTtlMs = new AtomicLong(DEFAULT_REMOTE_GRADLE_PROCESS_TTL_IN_MS);
private final Alarm myShutdownAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
private final AtomicInteger myCallsInProgressNumber = new AtomicInteger();
private final AtomicReference<ExternalSystemTaskNotificationListener> myNotificationListener =
new AtomicReference<ExternalSystemTaskNotificationListener>(new ExternalSystemTaskNotificationListenerAdapter() {});
@NotNull private final RemoteExternalSystemProjectResolverImpl<S> myProjectResolver;
@NotNull private final RemoteExternalSystemTaskManagerImpl<S> myTaskManager;
private volatile boolean myStdOutputConfigured;
public ExternalSystemFacadeImpl(@NotNull Class<ExternalSystemProjectResolver<S>> projectResolverClass,
@NotNull Class<ExternalSystemTaskManager<S>> buildManagerClass)
public AbstractExternalSystemFacadeImpl(@NotNull Class<ExternalSystemProjectResolver<S>> projectResolverClass,
@NotNull Class<ExternalSystemTaskManager<S>> buildManagerClass)
throws IllegalAccessException, InstantiationException
{
myProjectResolver = new RemoteExternalSystemProjectResolverImpl<S>(projectResolverClass.newInstance());
myTaskManager = new RemoteExternalSystemTaskManagerImpl<S>(buildManagerClass.newInstance());
updateAutoShutdownTime();
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
if (args.length < 1) {
throw new IllegalArgumentException(
"Can't create external system facade. Reason: given arguments don't contain information about external system resolver to use");
}
final Class<ExternalSystemProjectResolver<?>> resolverClass = (Class<ExternalSystemProjectResolver<?>>)Class.forName(args[0]);
if (!ExternalSystemProjectResolver.class.isAssignableFrom(resolverClass)) {
throw new IllegalArgumentException(String.format(
"Can't create external system facade. Reason: given external system resolver class (%s) must be IS-A '%s'",
resolverClass,
ExternalSystemProjectResolver.class));
}
if (args.length < 2) {
throw new IllegalArgumentException(
"Can't create external system facade. Reason: given arguments don't contain information about external system build manager to use"
);
}
final Class<ExternalSystemTaskManager<?>> buildManagerClass = (Class<ExternalSystemTaskManager<?>>)Class.forName(args[1]);
if (!ExternalSystemProjectResolver.class.isAssignableFrom(resolverClass)) {
throw new IllegalArgumentException(String.format(
"Can't create external system facade. Reason: given external system build manager (%s) must be IS-A '%s'",
buildManagerClass, ExternalSystemTaskManager.class
));
}
ExternalSystemFacadeImpl facade = new ExternalSystemFacadeImpl(resolverClass, buildManagerClass);
facade.init();
start(facade);
}
private void init() throws RemoteException {
protected void init() throws RemoteException {
applyProgressManager(RemoteExternalSystemProgressNotificationManager.NULL_OBJECT);
}
@Nullable
protected S getSettings() {
return mySettings.get();
}
@NotNull
protected ExternalSystemTaskNotificationListener getNotificationListener() {
return myNotificationListener.get();
}
@SuppressWarnings("unchecked")
@NotNull
@Override
public RemoteExternalSystemProjectResolver<S> getResolver() throws RemoteException, IllegalStateException {
try {
return getRemote(RemoteExternalSystemProjectResolver.class, myProjectResolver);
return getService(RemoteExternalSystemProjectResolver.class, myProjectResolver);
}
catch (Exception e) {
throw new IllegalStateException(String.format("Can't create '%s' service", RemoteExternalSystemProjectResolverImpl.class.getName()),
@@ -113,13 +73,48 @@ public class ExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings>
@Override
public RemoteExternalSystemTaskManager<S> getTaskManager() throws RemoteException {
try {
return getRemote(RemoteExternalSystemTaskManager.class, myTaskManager);
return getService(RemoteExternalSystemTaskManager.class, myTaskManager);
}
catch (Exception e) {
throw new IllegalStateException(String.format("Can't create '%s' service", ExternalSystemTaskManager.class.getName()), e);
}
}
@SuppressWarnings({"unchecked", "IOResourceOpenedButNotSafelyClosed", "UseOfSystemOutOrSystemErr"})
private <I extends RemoteExternalSystemService<S>, C extends I> I getService(@NotNull Class<I> interfaceClass,
@NotNull final C impl)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, RemoteException
{
Object cachedResult = myRemotes.get(interfaceClass);
if (cachedResult != null) {
return (I)cachedResult;
}
S settings = getSettings();
if (settings != null) {
impl.setNotificationListener(getNotificationListener());
impl.setSettings(settings);
}
impl.setNotificationListener(getNotificationListener());
try {
I created = createService(interfaceClass, impl);
I stored = (I)myRemotes.putIfAbsent(interfaceClass, created);
return stored == null ? created : stored;
}
catch (RemoteException e) {
Object raceResult = myRemotes.get(interfaceClass);
if (raceResult != null) {
// Race condition occurred
return (I)raceResult;
}
else {
throw new IllegalStateException(
String.format("Can't prepare remote service for interface '%s', implementation '%s'", interfaceClass, impl),
e
);
}
}
}
/**
* Generic method to retrieve exposed implementations of the target interface.
* <p/>
@@ -136,59 +131,9 @@ public class ExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings>
* @throws RemoteException
*/
@SuppressWarnings({"unchecked", "IOResourceOpenedButNotSafelyClosed", "UseOfSystemOutOrSystemErr"})
private <I extends RemoteExternalSystemService<S>, C extends I> I getRemote(@NotNull Class<I> interfaceClass,
@NotNull final C impl)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, RemoteException
{
Object cachedResult = myRemotes.get(interfaceClass);
if (cachedResult != null) {
return (I)cachedResult;
}
if (!myStdOutputConfigured) {
myStdOutputConfigured = true;
System.setOut(new LineAwarePrintStream(System.out));
System.setErr(new LineAwarePrintStream(System.err));
}
S settings = mySettings.get();
if (settings != null) {
impl.setNotificationListener(myNotificationListener.get());
impl.setSettings(settings);
}
impl.setNotificationListener(myNotificationListener.get());
I proxy = (I)Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { interfaceClass }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myCallsInProgressNumber.incrementAndGet();
try {
return method.invoke(impl, args);
}
finally {
myCallsInProgressNumber.decrementAndGet();
updateAutoShutdownTime();
}
}
});
try {
I stub = (I)UnicastRemoteObject.exportObject(proxy, 0);
I stored = (I)myRemotes.putIfAbsent(interfaceClass, stub);
return stored == null ? stub : stored;
}
catch (RemoteException e) {
Object raceResult = myRemotes.get(interfaceClass);
if (raceResult != null) {
// Race condition occurred
return (I)raceResult;
}
else {
throw new IllegalStateException(
String.format("Can't prepare remote service for interface '%s', implementation '%s'", interfaceClass, impl),
e
);
}
}
}
protected abstract <I extends RemoteExternalSystemService<S>, C extends I> I createService(@NotNull Class<I> interfaceClass,
@NotNull final C impl)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, RemoteException;
@Override
public boolean isTaskInProgress(@NotNull ExternalSystemTaskId id) throws RemoteException {
@@ -229,10 +174,6 @@ public class ExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings>
@Override
public void applySettings(@NotNull S settings) throws RemoteException {
mySettings.set(settings);
long ttl = settings.getRemoteProcessIdleTtlInMs();
if (ttl > 0) {
myTtlMs.set(ttl);
}
List<RemoteExternalSystemService<S>> services = ContainerUtilRt.newArrayList(myRemotes.values());
for (RemoteExternalSystemService<S> service : services) {
service.setSettings(settings);
@@ -247,26 +188,6 @@ public class ExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings>
myTaskManager.setNotificationListener(listener);
}
/**
* Schedules automatic process termination in {@code #REMOTE_GRADLE_PROCESS_TTL_IN_MS} milliseconds.
* <p/>
* Rationale: it's possible that IJ user performs gradle related activity (e.g. import from gradle) when the works purely
* at IJ. We don't want to keep remote process that communicates with the gradle api then.
*/
private void updateAutoShutdownTime() {
myShutdownAlarm.cancelAllRequests();
myShutdownAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (myCallsInProgressNumber.get() > 0) {
updateAutoShutdownTime();
return;
}
System.exit(0);
}
}, (int)myTtlMs.get());
}
private static class SwallowingNotificationListener implements ExternalSystemTaskNotificationListener {
@NotNull private final RemoteExternalSystemProgressNotificationManager myManager;
@@ -319,46 +240,4 @@ public class ExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings>
}
}
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private static class LineAwarePrintStream extends PrintStream {
private LineAwarePrintStream(@NotNull final PrintStream delegate) {
super(new OutputStream() {
@NotNull private final StringBuilder myBuffer = new StringBuilder();
@Override
public void write(int b) throws IOException {
char c = (char)b;
myBuffer.append(Character.toString(c));
if (c == '\n') {
doFlush();
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
int start = off;
int maxOffset = off + len;
for (int i = off; i < maxOffset; i++) {
if (b[i] == '\n') {
myBuffer.append(new String(b, start, i - start + 1));
doFlush();
start = i + 1;
}
}
if (start < maxOffset) {
myBuffer.append(new String(b, start, maxOffset - start));
}
}
private void doFlush() {
delegate.print(myBuffer.toString());
delegate.flush();
myBuffer.setLength(0);
}
});
}
}
}
@@ -0,0 +1,62 @@
/*
* 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.openapi.externalSystem.model.ProjectSystemId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* External system integration consists of common 'external system' functionality and external system-specific code. There are at
* least two approaches how to work with that external system-specific code:
* <pre>
* <ul>
* <li>use it from the ide process;</li>
* <li>create a slave process and perform external system-specific actions there;</li>
* </ul>
* </pre>
* <p/>
* E.g. when we work with particular external system api it might worth to do that at a separate process in order to avoid bad stuff
* like memory leaks to happen at the ide process. However, when external system-specific communication just starts new external
* system-process, there is no point in creating intermediate mediator process just to launch new process.
* <p/>
* That's why that stuff is covered by the current interface, i.e. different implementations are supposed to provide
* different 'in process' modes.
*
* @author Denis Zhdanov
* @since 8/9/13 3:21 PM
*/
public interface ExternalSystemCommunicationManager {
/**
* Creates new external system facade for the given arguments.
*
* @param id if for which new facade is to be created
* @param externalSystemId target external system id
* @return newly created facade for the given arguments (if it was possible to create one)
* @throws Exception in case something goes wrong
*/
@Nullable
RemoteExternalSystemFacade acquire(@NotNull String id, @NotNull ProjectSystemId externalSystemId)
throws Exception;
boolean isAlive(@NotNull RemoteExternalSystemFacade facade);
/**
* Disposes all resources acquired by the current manager.
*/
void clear();
}
@@ -1,53 +1,23 @@
package com.intellij.openapi.externalSystem.service;
import com.intellij.CommonBundle;
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.Disposable;
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.settings.ExternalSystemExecutionSettings;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
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.settings.ExternalSystemSettingsManager;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.IntegrationKey;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.project.ProjectManager;
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.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.psi.PsiBundle;
import com.intellij.util.Alarm;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.Consumer;
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;
@@ -57,14 +27,11 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -79,10 +46,6 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class ExternalSystemFacadeManager {
private static final Logger LOG = Logger.getInstance("#" + ExternalSystemFacadeManager.class.getName());
private static final String MAIN_CLASS_NAME = ExternalSystemFacadeImpl.class.getName();
private static final int REMOTE_FAIL_RECOVERY_ATTEMPTS_NUMBER = 3;
private final ConcurrentMap<IntegrationKey, RemoteExternalSystemFacade> myFacadeWrappers = ContainerUtil.newConcurrentMap();
@@ -90,43 +53,30 @@ public class ExternalSystemFacadeManager {
private final Map<IntegrationKey, Pair<RemoteExternalSystemFacade, ExternalSystemExecutionSettings>> myRemoteFacades
= ContainerUtil.newConcurrentMap();
@NotNull private final Lock myLock = new ReentrantLock();
@NotNull private final Lock myLock = new ReentrantLock();
@NotNull private final AtomicBoolean myInProcessCommunication = new AtomicBoolean();
private final AtomicReference<RemoteExternalSystemProgressNotificationManager> myExportedNotificationManager
= new AtomicReference<RemoteExternalSystemProgressNotificationManager>();
@NotNull private final AtomicReference<ExternalSystemCommunicationManager> myCommunicationManager =
new AtomicReference<ExternalSystemCommunicationManager>();
@NotNull private final ExternalSystemProgressNotificationManagerImpl myProgressManager;
@NotNull private final ExternalSystemSettingsManager mySettingsManager;
@NotNull private final RemoteProcessSupport<Object, RemoteExternalSystemFacade, String> mySupport;
@NotNull private final ThreadLocal<ProjectSystemId> myTargetExternalSystemId = new ThreadLocal<ProjectSystemId>();
@NotNull private final ExternalSystemSettingsManager mySettingsManager;
@NotNull private final RemoteExternalSystemProgressNotificationManager myProgressManager;
@NotNull private final RemoteExternalSystemCommunicationManager myRemoteCommunicationManager;
@NotNull private final InProcessExternalSystemCommunicationManager myInProcessCommunicationManager;
public ExternalSystemFacadeManager(@NotNull ExternalSystemSettingsManager settingsManager,
@NotNull ExternalSystemProgressNotificationManager notificationManager)
@NotNull ExternalSystemProgressNotificationManager notificationManager,
@NotNull RemoteExternalSystemCommunicationManager remoteCommunicationManager,
@NotNull InProcessExternalSystemCommunicationManager inProcessCommunicationManager)
{
mySettingsManager = settingsManager;
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();
}
};
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
public void run() {
shutdown(false);
}
});
myProgressManager = (RemoteExternalSystemProgressNotificationManager)notificationManager;
myRemoteCommunicationManager = remoteCommunicationManager;
myInProcessCommunicationManager = inProcessCommunicationManager;
boolean inProcessCommunication = Registry.is(ExternalSystemConstants.USE_IN_PROCESS_COMMUNICATION_REGISTRY_KEY, false);
myInProcessCommunication.set(inProcessCommunication);
myCommunicationManager.set(inProcessCommunication ? myInProcessCommunicationManager : myRemoteCommunicationManager);
}
@NotNull
@@ -140,93 +90,6 @@ public class ExternalSystemFacadeManager {
return projectManager.getDefaultProject();
}
private RunProfileState createRunProfileState() {
return new CommandLineState(null) {
private SimpleJavaParameters createJavaParameters() throws ExecutionException {
final SimpleJavaParameters params = new SimpleJavaParameters();
params.setJdk(new SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome()));
params.setWorkingDirectory(PathManager.getBinPath());
final List<String> classPath = ContainerUtilRt.newArrayList();
// IDE jars.
classPath.addAll(PathManager.getUtilClassPath());
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(ProjectBundle.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.enhanceParameters(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;
}
};
}
public synchronized void shutdown(boolean wait) {
mySupport.stopAll(wait);
}
public void onProjectRename(@NotNull String oldName, @NotNull String newName) {
onProjectRename(myFacadeWrappers, oldName, newName);
onProjectRename(myRemoteFacades, oldName, newName);
@@ -254,9 +117,9 @@ public class ExternalSystemFacadeManager {
}
}
}
/**
* @return gradle api facade to use
* @return gradle api facade to use
* @throws Exception in case of inability to return the facade
*/
@NotNull
@@ -271,7 +134,8 @@ public class ExternalSystemFacadeManager {
final RemoteExternalSystemFacade facade = myFacadeWrappers.get(key);
if (facade == null) {
final RemoteExternalSystemFacade newFacade = (RemoteExternalSystemFacade)Proxy.newProxyInstance(
ExternalSystemFacadeManager.class.getClassLoader(), new Class[]{RemoteExternalSystemFacade.class, Consumer.class}, new MyHandler(key)
ExternalSystemFacadeManager.class.getClassLoader(), new Class[]{RemoteExternalSystemFacade.class, Consumer.class},
new MyHandler(key)
);
myFacadeWrappers.putIfAbsent(key, newFacade);
}
@@ -299,6 +163,12 @@ public class ExternalSystemFacadeManager {
@SuppressWarnings("ConstantConditions")
@NotNull
private RemoteExternalSystemFacade doGetFacade(@NotNull IntegrationKey key, @NotNull Project project) throws Exception {
boolean currentInProcess = Registry.is(ExternalSystemConstants.USE_IN_PROCESS_COMMUNICATION_REGISTRY_KEY, false);
if (myInProcessCommunication.getAndSet(currentInProcess) != currentInProcess) {
myCommunicationManager.get().clear();
myCommunicationManager.set(currentInProcess ? myInProcessCommunicationManager : myRemoteCommunicationManager);
}
ExternalSystemManager manager = ExternalSystemApiUtil.getManager(key.getExternalSystemId());
if (project.isDisposed() || manager == null) {
return RemoteExternalSystemFacade.NULL_OBJECT;
@@ -315,7 +185,7 @@ public class ExternalSystemFacadeManager {
return pair.first;
}
if (pair != null) {
mySupport.stopAll(true);
myCommunicationManager.get().clear();
myFacadeWrappers.clear();
myRemoteFacades.clear();
}
@@ -329,16 +199,14 @@ public class ExternalSystemFacadeManager {
@SuppressWarnings("unchecked")
@NotNull
private RemoteExternalSystemFacade doCreateFacade(@NotNull IntegrationKey key, @NotNull Project project) throws Exception {
myTargetExternalSystemId.set(key.getExternalSystemId());
final RemoteExternalSystemFacade facade = mySupport.acquire(this, project.getName());
myTargetExternalSystemId.set(null);
final RemoteExternalSystemFacade facade = myCommunicationManager.get().acquire(project.getName(), key.getExternalSystemId());
if (facade == null) {
throw new IllegalStateException("Can't obtain facade to working with gradle api at the remote process. Project: " + project);
throw new IllegalStateException("Can't obtain facade to working with external api at the remote process. Project: " + project);
}
Disposer.register(project, new Disposable() {
@Override
public void dispose() {
mySupport.stopAll(true);
myCommunicationManager.get().clear();
myFacadeWrappers.clear();
myRemoteFacades.clear();
}
@@ -349,22 +217,6 @@ public class ExternalSystemFacadeManager {
Pair<RemoteExternalSystemFacade, ExternalSystemExecutionSettings> newPair = Pair.create(result, settings);
myRemoteFacades.put(key, newPair);
result.applySettings(newPair.second);
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 {
result.applyProgressManager(exported);
}
return result;
}
@@ -373,10 +225,10 @@ public class ExternalSystemFacadeManager {
@NotNull IntegrationKey key,
@NotNull Pair<RemoteExternalSystemFacade, ExternalSystemExecutionSettings> pair)
{
// Check if remote process is alive.
if (!myCommunicationManager.get().isAlive(pair.first)) {
return false;
}
try {
pair.first.getResolver();
ExternalSystemExecutionSettings currentSettings
= mySettingsManager.getExecutionSettings(project, key.getExternalProjectConfigPath(), key.getExternalSystemId());
if (!currentSettings.equals(pair.second)) {
@@ -0,0 +1,53 @@
/*
* 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.openapi.externalSystem.ExternalSystemManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.service.remote.wrapper.ExternalSystemFacadeWrapper;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Denis Zhdanov
* @since 8/9/13 4:00 PM
*/
public class InProcessExternalSystemCommunicationManager implements ExternalSystemCommunicationManager {
@SuppressWarnings("unchecked")
@Nullable
@Override
public RemoteExternalSystemFacade acquire(@NotNull String id, @NotNull ProjectSystemId externalSystemId) throws Exception {
ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
assert manager != null;
return new InProcessExternalSystemFacadeImpl(manager.getProjectResolverClass(), manager.getTaskManagerClass());
}
@Override
public boolean isAlive(@NotNull RemoteExternalSystemFacade facade) {
RemoteExternalSystemFacade toCheck = facade;
if (facade instanceof ExternalSystemFacadeWrapper) {
toCheck = ((ExternalSystemFacadeWrapper)facade).getDelegate();
}
return toCheck instanceof InProcessExternalSystemFacadeImpl;
}
@Override
public void clear() {
}
}
@@ -0,0 +1,44 @@
/*
* 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.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings;
import com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver;
import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
import org.jetbrains.annotations.NotNull;
import java.rmi.RemoteException;
/**
* @author Denis Zhdanov
* @since 8/9/13 5:42 PM
*/
public class InProcessExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings> extends AbstractExternalSystemFacadeImpl<S> {
public InProcessExternalSystemFacadeImpl(@NotNull Class<ExternalSystemProjectResolver<S>> projectResolverClass,
@NotNull Class<ExternalSystemTaskManager<S>> buildManagerClass)
throws IllegalAccessException, InstantiationException
{
super(projectResolverClass, buildManagerClass);
}
@Override
protected <I extends RemoteExternalSystemService<S>, C extends I> I createService(@NotNull Class<I> interfaceClass, @NotNull C impl)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, RemoteException
{
return impl;
}
}
@@ -0,0 +1,257 @@
/*
* 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.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.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.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();
}
};
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
public void run() {
shutdown(false);
}
});
}
public synchronized void shutdown(boolean wait) {
mySupport.stopAll(wait);
}
private RunProfileState createRunProfileState() {
return new CommandLineState(null) {
private SimpleJavaParameters createJavaParameters() throws ExecutionException {
final SimpleJavaParameters params = new SimpleJavaParameters();
params.setJdk(new SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome()));
params.setWorkingDirectory(PathManager.getBinPath());
final List<String> classPath = ContainerUtilRt.newArrayList();
// IDE jars.
classPath.addAll(PathManager.getUtilClassPath());
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(ProjectBundle.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.enhanceParameters(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 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);
}
}
@@ -0,0 +1,188 @@
/*
* 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.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings;
import com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver;
import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Denis Zhdanov
* @since 8/9/13 4:28 PM
*/
public class RemoteExternalSystemFacadeImpl<S extends ExternalSystemExecutionSettings> extends AbstractExternalSystemFacadeImpl<S> {
private static final long DEFAULT_REMOTE_PROCESS_TTL_IN_MS = TimeUnit.MILLISECONDS.convert(3, TimeUnit.MINUTES);
private final AtomicInteger myCallsInProgressNumber = new AtomicInteger();
private final Alarm myShutdownAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
private final AtomicLong myTtlMs = new AtomicLong(DEFAULT_REMOTE_PROCESS_TTL_IN_MS);
private volatile boolean myStdOutputConfigured;
public RemoteExternalSystemFacadeImpl(@NotNull Class<ExternalSystemProjectResolver<S>> projectResolverClass,
@NotNull Class<ExternalSystemTaskManager<S>> buildManagerClass)
throws IllegalAccessException, InstantiationException
{
super(projectResolverClass, buildManagerClass);
updateAutoShutdownTime();
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
if (args.length < 1) {
throw new IllegalArgumentException(
"Can't create external system facade. Reason: given arguments don't contain information about external system resolver to use");
}
final Class<ExternalSystemProjectResolver<?>> resolverClass = (Class<ExternalSystemProjectResolver<?>>)Class.forName(args[0]);
if (!ExternalSystemProjectResolver.class.isAssignableFrom(resolverClass)) {
throw new IllegalArgumentException(String.format(
"Can't create external system facade. Reason: given external system resolver class (%s) must be IS-A '%s'",
resolverClass,
ExternalSystemProjectResolver.class));
}
if (args.length < 2) {
throw new IllegalArgumentException(
"Can't create external system facade. Reason: given arguments don't contain information about external system build manager to use"
);
}
final Class<ExternalSystemTaskManager<?>> buildManagerClass = (Class<ExternalSystemTaskManager<?>>)Class.forName(args[1]);
if (!ExternalSystemProjectResolver.class.isAssignableFrom(resolverClass)) {
throw new IllegalArgumentException(String.format(
"Can't create external system facade. Reason: given external system build manager (%s) must be IS-A '%s'",
buildManagerClass, ExternalSystemTaskManager.class
));
}
RemoteExternalSystemFacadeImpl facade = new RemoteExternalSystemFacadeImpl(resolverClass, buildManagerClass);
facade.init();
start(facade);
}
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed", "unchecked", "UseOfSystemOutOrSystemErr"})
@Override
protected <I extends RemoteExternalSystemService<S>, C extends I> I createService(@NotNull Class<I> interfaceClass, @NotNull final C impl)
throws ClassNotFoundException, IllegalAccessException, InstantiationException, RemoteException
{
if (!myStdOutputConfigured) {
myStdOutputConfigured = true;
System.setOut(new LineAwarePrintStream(System.out));
System.setErr(new LineAwarePrintStream(System.err));
}
I proxy = (I)Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{interfaceClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myCallsInProgressNumber.incrementAndGet();
try {
return method.invoke(impl, args);
}
finally {
myCallsInProgressNumber.decrementAndGet();
updateAutoShutdownTime();
}
}
});
return (I)UnicastRemoteObject.exportObject(proxy, 0);
}
@Override
public void applySettings(@NotNull S settings) throws RemoteException {
super.applySettings(settings);
long ttl = settings.getRemoteProcessIdleTtlInMs();
if (ttl > 0) {
myTtlMs.set(ttl);
}
}
/**
* Schedules automatic process termination in {@code #REMOTE_GRADLE_PROCESS_TTL_IN_MS} milliseconds.
* <p/>
* Rationale: it's possible that IJ user performs gradle related activity (e.g. import from gradle) when the works purely
* at IJ. We don't want to keep remote process that communicates with the gradle api then.
*/
private void updateAutoShutdownTime() {
myShutdownAlarm.cancelAllRequests();
myShutdownAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (myCallsInProgressNumber.get() > 0) {
updateAutoShutdownTime();
return;
}
System.exit(0);
}
}, (int)myTtlMs.get());
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private static class LineAwarePrintStream extends PrintStream {
private LineAwarePrintStream(@NotNull final PrintStream delegate) {
super(new OutputStream() {
@NotNull private final StringBuilder myBuffer = new StringBuilder();
@Override
public void write(int b) throws IOException {
char c = (char)b;
myBuffer.append(Character.toString(c));
if (c == '\n') {
doFlush();
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
int start = off;
int maxOffset = off + len;
for (int i = off; i < maxOffset; i++) {
if (b[i] == '\n') {
myBuffer.append(new String(b, start, i - start + 1));
doFlush();
start = i + 1;
}
}
if (start < maxOffset) {
myBuffer.append(new String(b, start, maxOffset - start));
}
}
private void doFlush() {
delegate.print(myBuffer.toString());
delegate.flush();
myBuffer.setLength(0);
}
});
}
}
}
@@ -35,6 +35,11 @@ public class ExternalSystemFacadeWrapper<S extends ExternalSystemExecutionSettin
myProgressManager = progressManager;
}
@NotNull
public RemoteExternalSystemFacade<S> getDelegate() {
return myDelegate;
}
@NotNull
@Override
public RemoteExternalSystemProjectResolver<S> getResolver() throws RemoteException, IllegalStateException {
@@ -309,3 +309,5 @@ linux.native.menu=false
linux.native.menu.description=Enables native menu on Ubuntu
windows.jumplist=false
windows.jumplist.description=Enables JumpLists on Windows
external.system.in.process=false
@@ -5,6 +5,8 @@
<!--Generic services-->
<applicationService serviceImplementation="com.intellij.openapi.externalSystem.service.ExternalSystemFacadeManager"/>
<applicationService serviceImplementation="com.intellij.openapi.externalSystem.service.RemoteExternalSystemCommunicationManager"/>
<applicationService serviceImplementation="com.intellij.openapi.externalSystem.service.InProcessExternalSystemCommunicationManager"/>
<applicationService
serviceInterface="com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager"
serviceImplementation="com.intellij.openapi.externalSystem.service.remote.ExternalSystemProgressNotificationManagerImpl"/>