mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-111831 External system: Make it possible to communicate with external system at the ide process
This commit is contained in:
+2
@@ -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";
|
||||
|
||||
|
||||
+56
-177
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -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();
|
||||
}
|
||||
+37
-185
@@ -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)) {
|
||||
|
||||
+53
@@ -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() {
|
||||
}
|
||||
}
|
||||
+44
@@ -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;
|
||||
}
|
||||
}
|
||||
+257
@@ -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);
|
||||
}
|
||||
}
|
||||
+188
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user