blob: 7ef16bdcfc4efdedb4e4184667be82434846b87c [file] [log] [blame]
/*
* Copyright 2000-2012 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 org.jetbrains.idea.maven.server;
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.*;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.rmi.RemoteProcessSupport;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.*;
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Alarm;
import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import org.apache.lucene.search.Query;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.execution.MavenRunnerSettings;
import org.jetbrains.idea.maven.model.MavenExplicitProfiles;
import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.model.MavenModel;
import org.jetbrains.idea.maven.project.MavenConsole;
import org.jetbrains.idea.maven.project.MavenGeneralSettings;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.utils.MavenLog;
import org.jetbrains.idea.maven.utils.MavenProgressIndicator;
import org.jetbrains.idea.maven.utils.MavenUtil;
import java.io.File;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@State(
name = "MavenVersion",
storages = {@Storage(
file = StoragePathMacros.APP_CONFIG + "/mavenVersion.xml")})
public class MavenServerManager extends RemoteObjectWrapper<MavenServer> implements PersistentStateComponent<Element> {
@NonNls private static final String MAIN_CLASS = "org.jetbrains.idea.maven.server.RemoteMavenServer";
private static final String DEFAULT_VM_OPTIONS = "-Xmx512m";
private final RemoteProcessSupport<Object, MavenServer, Object> mySupport;
private final RemoteMavenServerLogger myLogger = new RemoteMavenServerLogger();
private final RemoteMavenServerDownloadListener myDownloadListener = new RemoteMavenServerDownloadListener();
private boolean myLoggerExported;
private boolean myDownloadListenerExported;
private final Alarm myShutdownAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
private boolean useMaven2 = false;
private String mavenEmbedderVMOptions = DEFAULT_VM_OPTIONS;
private String embedderJdk = MavenRunnerSettings.USE_INTERNAL_JAVA;
public static MavenServerManager getInstance() {
return ServiceManager.getService(MavenServerManager.class);
}
public MavenServerManager() {
super(null);
mySupport = new RemoteProcessSupport<Object, MavenServer, Object>(MavenServer.class) {
@Override
protected void fireModificationCountChanged() {
}
@Override
protected String getName(Object file) {
return MavenServerManager.class.getSimpleName();
}
@Override
protected RunProfileState getRunProfileState(Object target, Object configuration, Executor executor) throws ExecutionException {
return createRunProfileState();
}
};
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
public void run() {
shutdown(false);
}
});
}
@NotNull
protected synchronized MavenServer create() throws RemoteException {
MavenServer result;
try {
result = mySupport.acquire(this, "");
}
catch (Exception e) {
throw new RemoteException("Cannot start maven service", e);
}
myLoggerExported = UnicastRemoteObject.exportObject(myLogger, 0) != null;
if (!myLoggerExported) throw new RemoteException("Cannot export logger object");
myDownloadListenerExported = UnicastRemoteObject.exportObject(myDownloadListener, 0) != null;
if (!myDownloadListenerExported) throw new RemoteException("Cannot export download listener object");
result.set(myLogger, myDownloadListener);
return result;
}
public synchronized void shutdown(boolean wait) {
mySupport.stopAll(wait);
cleanup();
}
protected synchronized void cleanup() {
super.cleanup();
if (myLoggerExported) {
try {
UnicastRemoteObject.unexportObject(myLogger, true);
}
catch (RemoteException e) {
MavenLog.LOG.error(e);
}
myLoggerExported = false;
}
if (myDownloadListenerExported) {
try {
UnicastRemoteObject.unexportObject(myDownloadListener, true);
}
catch (RemoteException e) {
MavenLog.LOG.error(e);
}
myDownloadListenerExported = false;
}
myShutdownAlarm.cancelAllRequests();
}
@NotNull
private Sdk getJdk() {
if (embedderJdk.equals(MavenRunnerSettings.USE_JAVA_HOME)) {
final String javaHome = System.getenv("JAVA_HOME");
if (!StringUtil.isEmptyOrSpaces(javaHome)) {
Sdk jdk = JavaSdk.getInstance().createJdk("", javaHome);
if (jdk != null) {
return jdk;
}
}
}
for (Sdk projectJdk : ProjectJdkTable.getInstance().getAllJdks()) {
if (projectJdk.getName().equals(embedderJdk)) {
return projectJdk;
}
}
// By default use internal jdk
return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
}
private RunProfileState createRunProfileState() {
return new CommandLineState(null) {
private SimpleJavaParameters createJavaParameters() throws ExecutionException {
final SimpleJavaParameters params = new SimpleJavaParameters();
params.setJdk(getJdk());
params.setWorkingDirectory(PathManager.getBinPath());
final List<String> classPath = new ArrayList<String>();
classPath.addAll(PathManager.getUtilClassPath());
ContainerUtil.addIfNotNull(PathUtil.getJarPathForClass(Query.class), classPath);
params.getClassPath().add(PathManager.getResourceRoot(getClass(), "/messages/CommonBundle.properties"));
params.getClassPath().addAll(classPath);
params.getClassPath().addAllFiles(collectClassPathAndLibsFolder());
params.setMainClass(MAIN_CLASS);
Map<String, String> defs = new THashMap<String, String>();
defs.putAll(MavenUtil.getPropertiesFromMavenOpts());
// pass ssl-related options
for (Map.Entry<Object, Object> each : System.getProperties().entrySet()) {
Object key = each.getKey();
Object value = each.getValue();
if (key instanceof String && value instanceof String && ((String)key).startsWith("javax.net.ssl")) {
defs.put((String)key, (String)value);
}
}
if (SystemInfo.isMac) {
String arch = System.getProperty("sun.arch.data.model");
if (arch != null) {
params.getVMParametersList().addParametersString("-d" + arch);
}
}
defs.put("java.awt.headless", "true");
for (Map.Entry<String, String> each : defs.entrySet()) {
params.getVMParametersList().defineProperty(each.getKey(), each.getValue());
}
params.getVMParametersList().addProperty("idea.version=", MavenUtil.getIdeaVersionToPassToMavenProcess());
boolean xmxSet = false;
if (mavenEmbedderVMOptions != null) {
ParametersList mavenOptsList = new ParametersList();
mavenOptsList.addParametersString(mavenEmbedderVMOptions);
for (String param : mavenOptsList.getParameters()) {
if (param.startsWith("-Xmx")) {
xmxSet = true;
}
params.getVMParametersList().add(param);
}
}
String embedderXmx = System.getProperty("idea.maven.embedder.xmx");
if (embedderXmx != null) {
params.getVMParametersList().add("-Xmx" + embedderXmx);
}
else {
if (!xmxSet) {
params.getVMParametersList().add("-Xmx512m");
}
}
String mavenEmbedderDebugPort = System.getProperty("idea.maven.embedder.debug.port");
if (mavenEmbedderDebugPort != null) {
params.getVMParametersList().addParametersString("-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + mavenEmbedderDebugPort);
}
String mavenEmbedderParameters = System.getProperty("idea.maven.embedder.parameters");
if (mavenEmbedderParameters != null) {
params.getProgramParametersList().addParametersString(mavenEmbedderParameters);
}
return params;
}
@Override
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();
GeneralCommandLine commandLine =
JdkUtil.setupJVMCommandLine(((JavaSdkType)sdk.getSdkType()).getVMExecutablePath(sdk), params, false);
OSProcessHandler processHandler =
new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString(), commandLine.getCharset());
processHandler.setShouldDestroyProcessRecursively(false);
return processHandler;
}
};
}
public static File getMavenLibDirectory() {
File pluginFileOrDir = new File(PathUtil.getJarPathForClass(MavenServerManager.class));
if (pluginFileOrDir.isDirectory()) {
File luceneLib = new File(PathUtil.getJarPathForClass(Query.class));
return new File(luceneLib.getParentFile().getParentFile().getParentFile(), "maven3-server-impl/lib/maven3/lib");
}
return new File(pluginFileOrDir.getParentFile(), "maven3");
}
public List<File> collectClassPathAndLibsFolder() {
File pluginFileOrDir = new File(PathUtil.getJarPathForClass(MavenServerManager.class));
List<File> classpath = new ArrayList<File>();
String root = pluginFileOrDir.getParent();
boolean useMaven2 = this.useMaven2;
if (ApplicationManager.getApplication().isUnitTestMode()) {
useMaven2 = true;
}
if (pluginFileOrDir.isDirectory()) {
classpath.add(new File(root, "maven-server-api"));
File luceneLib = new File(PathUtil.getJarPathForClass(Query.class));
if (useMaven2) {
classpath.add(new File(root, "maven2-server-impl"));
addDir(classpath, new File(luceneLib.getParentFile().getParentFile().getParentFile(), "maven2-server-impl/lib"));
}
else {
classpath.add(new File(root, "maven3-server-impl"));
File maven3Module_Lib = new File(luceneLib.getParentFile().getParentFile().getParentFile(), "maven3-server-impl/lib");
addDir(classpath, maven3Module_Lib);
File maven3Home = new File(maven3Module_Lib, "maven3");
addDir(classpath, new File(maven3Home, "lib"));
classpath.add(new File(maven3Home, "boot/plexus-classworlds-2.4.jar"));
}
}
else {
classpath.add(new File(root, "maven-server-api.jar"));
if (useMaven2) {
classpath.add(new File(root, "maven2-server-impl.jar"));
addDir(classpath, new File(root, "maven2"));
}
else {
classpath.add(new File(root, "maven3-server-impl.jar"));
addDir(classpath, new File(root, "maven3"));
}
}
return classpath;
}
private static void addDir(List<File> classpath, File dir) {
for (File jar : dir.listFiles()) {
if (jar.isFile() && jar.getName().endsWith(".jar")) {
classpath.add(jar);
}
}
}
public MavenEmbedderWrapper createEmbedder(final Project project, final boolean alwaysOnline) {
return new MavenEmbedderWrapper(this) {
@NotNull
@Override
protected MavenServerEmbedder create() throws RemoteException {
MavenServerSettings settings = convertSettings(MavenProjectsManager.getInstance(project).getGeneralSettings());
if (alwaysOnline && settings.isOffline()) {
settings = settings.clone();
settings.setOffline(false);
}
settings.setProjectJdk(MavenUtil.getSdkPath(ProjectRootManager.getInstance(project).getProjectSdk()));
return MavenServerManager.this.getOrCreateWrappee().createEmbedder(settings);
}
};
}
public MavenIndexerWrapper createIndexer() {
return new MavenIndexerWrapper(this) {
@NotNull
@Override
protected MavenServerIndexer create() throws RemoteException {
return MavenServerManager.this.getOrCreateWrappee().createIndexer();
}
};
}
public MavenModel interpolateAndAlignModel(final MavenModel model, final File basedir) {
return perform(new Retriable<MavenModel>() {
@Override
public MavenModel execute() throws RemoteException {
return getOrCreateWrappee().interpolateAndAlignModel(model, basedir);
}
});
}
public MavenModel assembleInheritance(final MavenModel model, final MavenModel parentModel) {
return perform(new Retriable<MavenModel>() {
@Override
public MavenModel execute() throws RemoteException {
return getOrCreateWrappee().assembleInheritance(model, parentModel);
}
});
}
public ProfileApplicationResult applyProfiles(final MavenModel model,
final File basedir,
final MavenExplicitProfiles explicitProfiles,
final Collection<String> alwaysOnProfiles) {
return perform(new Retriable<ProfileApplicationResult>() {
@Override
public ProfileApplicationResult execute() throws RemoteException {
return getOrCreateWrappee().applyProfiles(model, basedir, explicitProfiles, alwaysOnProfiles);
}
});
}
public void addDownloadListener(MavenServerDownloadListener listener) {
myDownloadListener.myListeners.add(listener);
}
public void removeDownloadListener(MavenServerDownloadListener listener) {
myDownloadListener.myListeners.remove(listener);
}
public static MavenServerSettings convertSettings(MavenGeneralSettings settings) {
MavenServerSettings result = new MavenServerSettings();
result.setLoggingLevel(settings.getOutputLevel().getLevel());
result.setOffline(settings.isWorkOffline());
result.setMavenHome(settings.getEffectiveMavenHome());
result.setUserSettingsFile(settings.getEffectiveUserSettingsIoFile());
result.setGlobalSettingsFile(settings.getEffectiveGlobalSettingsIoFile());
result.setLocalRepository(settings.getEffectiveLocalRepository());
result.setPluginUpdatePolicy(settings.getPluginUpdatePolicy().getServerPolicy());
result.setSnapshotUpdatePolicy(settings.isAlwaysUpdateSnapshots() ? MavenServerSettings.UpdatePolicy.ALWAYS_UPDATE : MavenServerSettings.UpdatePolicy.DO_NOT_UPDATE);
return result;
}
public static MavenServerConsole wrapAndExport(final MavenConsole console) {
try {
RemoteMavenServerConsole result = new RemoteMavenServerConsole(console);
UnicastRemoteObject.exportObject(result, 0);
return result;
}
catch (RemoteException e) {
throw new RuntimeException(e);
}
}
public static MavenServerProgressIndicator wrapAndExport(final MavenProgressIndicator process) {
try {
RemoteMavenServerProgressIndicator result = new RemoteMavenServerProgressIndicator(process);
UnicastRemoteObject.exportObject(result, 0);
return result;
}
catch (RemoteException e) {
throw new RuntimeException(e);
}
}
public static MavenServerIndicesProcessor wrapAndExport(final MavenIndicesProcessor processor) {
try {
RemoteMavenServerIndicesProcessor result = new RemoteMavenServerIndicesProcessor(processor);
UnicastRemoteObject.exportObject(result, 0);
return result;
}
catch (RemoteException e) {
throw new RuntimeException(e);
}
}
public boolean isUseMaven2() {
return useMaven2;
}
public void setUseMaven2(boolean useMaven2) {
if (this.useMaven2 != useMaven2) {
this.useMaven2 = useMaven2;
shutdown(false);
}
}
@NotNull
public String getMavenEmbedderVMOptions() {
return mavenEmbedderVMOptions;
}
public void setMavenEmbedderVMOptions(@NotNull String mavenEmbedderVMOptions) {
if (!mavenEmbedderVMOptions.trim().equals(this.mavenEmbedderVMOptions.trim())) {
this.mavenEmbedderVMOptions = mavenEmbedderVMOptions;
shutdown(false);
}
}
@NotNull
public String getEmbedderJdk() {
return embedderJdk;
}
public void setEmbedderJdk(@NotNull String embedderJdk) {
if (!this.embedderJdk.equals(embedderJdk)) {
this.embedderJdk = embedderJdk;
shutdown(false);
}
}
@Nullable
@Override
public Element getState() {
final Element element = new Element("maven-version");
element.setAttribute("version", useMaven2 ? "2.x" : "3.x");
element.setAttribute("vmOptions", mavenEmbedderVMOptions);
element.setAttribute("embedderJdk", embedderJdk);
return element;
}
@Override
public void loadState(Element state) {
String version = state.getAttributeValue("version");
useMaven2 = "2.x".equals(version);
String vmOptions = state.getAttributeValue("vmOptions");
mavenEmbedderVMOptions = vmOptions == null ? DEFAULT_VM_OPTIONS : vmOptions;
String embedderJdk = state.getAttributeValue("embedderJdk");
this.embedderJdk = embedderJdk == null ? MavenRunnerSettings.USE_INTERNAL_JAVA : embedderJdk;
}
private static class RemoteMavenServerLogger extends MavenRemoteObject implements MavenServerLogger {
public void info(Throwable e) {
MavenLog.LOG.info(e);
}
public void warn(Throwable e) {
MavenLog.LOG.warn(e);
}
public void error(Throwable e) {
MavenLog.LOG.error(e);
}
public void print(String s) {
//noinspection UseOfSystemOutOrSystemErr
System.out.println(s);
}
}
private static class RemoteMavenServerDownloadListener extends MavenRemoteObject implements MavenServerDownloadListener {
private final List<MavenServerDownloadListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
public void artifactDownloaded(File file, String relativePath) throws RemoteException {
for (MavenServerDownloadListener each : myListeners) {
each.artifactDownloaded(file, relativePath);
}
}
}
private static class RemoteMavenServerProgressIndicator extends MavenRemoteObject implements MavenServerProgressIndicator {
private final MavenProgressIndicator myProcess;
public RemoteMavenServerProgressIndicator(MavenProgressIndicator process) {
myProcess = process;
}
public void setText(String text) {
myProcess.setText(text);
}
public void setText2(String text) {
myProcess.setText2(text);
}
public boolean isCanceled() {
return myProcess.isCanceled();
}
public void setIndeterminate(boolean value) {
myProcess.getIndicator().setIndeterminate(value);
}
public void setFraction(double fraction) {
myProcess.setFraction(fraction);
}
}
private static class RemoteMavenServerConsole extends MavenRemoteObject implements MavenServerConsole {
private final MavenConsole myConsole;
public RemoteMavenServerConsole(MavenConsole console) {
myConsole = console;
}
public void printMessage(int level, String message, Throwable throwable) {
myConsole.printMessage(level, message, throwable);
}
}
private static class RemoteMavenServerIndicesProcessor extends MavenRemoteObject implements MavenServerIndicesProcessor {
private final MavenIndicesProcessor myProcessor;
private RemoteMavenServerIndicesProcessor(MavenIndicesProcessor processor) {
myProcessor = processor;
}
@Override
public void processArtifacts(Collection<MavenId> artifacts) {
myProcessor.processArtifacts(artifacts);
}
}
}