/* * Copyright 2013-2017 Grzegorz Ligas <ligasgr@gmail.com> and other contributors * (see the CONTRIBUTORS file). * * 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.intellij.xquery.runner; import com.intellij.execution.CantRunException; import com.intellij.execution.CommonProgramRunConfigurationParameters; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.ModuleBasedConfiguration; import com.intellij.execution.configurations.RunConfigurationModule; import com.intellij.execution.configurations.SimpleJavaParameters; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessHandlerFactory; import com.intellij.execution.process.ProcessTerminatedListener; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.util.ProgramParametersUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdkType; import com.intellij.openapi.projectRoots.JdkUtil; import com.intellij.openapi.projectRoots.ProjectJdkTable; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkTypeId; import com.intellij.openapi.projectRoots.SimpleJavaSdkType; import com.intellij.openapi.projectRoots.impl.SdkVersionUtil; import com.intellij.openapi.roots.JdkOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEnumerator; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.OrderRootsEnumerator; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingProjectManager; import com.intellij.util.text.VersionComparatorUtil; import org.intellij.xquery.runner.state.run.DataSourceAccessor; import org.intellij.xquery.runner.state.run.VariablesAccessor; import org.intellij.xquery.runner.state.run.XQueryRunConfiguration; import org.intellij.xquery.runner.state.run.XQueryRunConfigurationSerializer; import org.intellij.xquery.runner.state.run.XmlConfigurationAccessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileWriter; import java.nio.charset.Charset; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; public class XQueryRunProfileState extends CommandLineState { private XQueryRunConfiguration configuration; private int port; private SimpleJavaParameters myParams; public XQueryRunProfileState(ExecutionEnvironment environment, XQueryRunConfiguration runConfiguration) { super(environment); configuration = runConfiguration; } public void setPort(int port) { this.port = port; } @NotNull @Override protected ProcessHandler startProcess() throws ExecutionException { ProcessHandlerFactory factory = ProcessHandlerFactory.getInstance(); OSProcessHandler processHandler = factory.createProcessHandler(createCommandLine()); ProcessTerminatedListener.attach(processHandler); return processHandler; } private SimpleJavaParameters getJavaParameters() throws ExecutionException { if (myParams == null) { myParams = createJavaParameters(); } return myParams; } private SimpleJavaParameters createJavaParameters() throws ExecutionException { final SimpleJavaParameters parameters = prepareRunnerParameters(); configureJreRelatedParameters(parameters); return parameters; } private void configureJreRelatedParameters(SimpleJavaParameters parameters) throws CantRunException { final RunConfigurationModule module = configuration.getConfigurationModule(); final String jreHome = configuration.isAlternativeJrePathEnabled() ? configuration.getAlternativeJrePath() : null; configureModule(module, parameters, jreHome); configureConfiguration(parameters, configuration); } private void configureModule(final RunConfigurationModule runConfigurationModule, final SimpleJavaParameters parameters, @Nullable String jreHome) throws CantRunException { Module module = runConfigurationModule.getModule(); if (module == null) { throw CantRunException.noModuleConfigured(runConfigurationModule.getModuleName()); } configureByModule(parameters, module, createModuleJdk(module, jreHome)); } private void configureByModule(SimpleJavaParameters parameters, final Module module, final Sdk jdk) throws CantRunException { if (jdk == null) { throw CantRunException.noJdkConfigured(); } parameters.setJdk(jdk); setDefaultCharset(parameters, module.getProject()); configureEnumerator(OrderEnumerator.orderEntries(module).runtimeOnly().recursively(), jdk).collectPaths(parameters.getClassPath()); } private void setDefaultCharset(SimpleJavaParameters parameters, final Project project) { Charset encoding = EncodingProjectManager.getInstance(project).getDefaultCharset(); parameters.setCharset(encoding); } private OrderRootsEnumerator configureEnumerator(OrderEnumerator enumerator, Sdk jdk) { enumerator = enumerator.productionOnly(); OrderRootsEnumerator rootsEnumerator = enumerator.classes(); rootsEnumerator = rootsEnumerator.usingCustomRootProvider(e -> e instanceof JdkOrderEntry ? jdkRoots(jdk) : e.getFiles(OrderRootType.CLASSES)); return rootsEnumerator; } private Sdk createModuleJdk(final Module module, @Nullable String jreHome) throws CantRunException { return jreHome == null ? getValidJdkToRunModule(module) : createAlternativeJdk(jreHome); } private SimpleJavaParameters prepareRunnerParameters() throws CantRunException { final SimpleJavaParameters parameters = new SimpleJavaParameters(); parameters.setMainClass(configuration.getRunClass()); boolean isDebugging = getEnvironment().getExecutor().getId().equals(DefaultDebugExecutor.EXECUTOR_ID); parameters.getProgramParametersList().prepend(getSerializedConfig(configuration, isDebugging, port).getAbsolutePath()); parameters.getClassPath().addFirst(new XQueryRunnerClasspathEntryGenerator().generateRunnerClasspathEntries(configuration)); return parameters; } private File getSerializedConfig(XQueryRunConfiguration configuration, boolean isDebugging, int port) { try { File serializedConfig = File.createTempFile("xquery-run", ".xml"); XmlConfigurationAccessor xmlConfigurationAccessor = new XmlConfigurationAccessor(); VariablesAccessor variablesAccessor = new VariablesAccessor(); DataSourceAccessor dataSourceAccessor = new DataSourceAccessor(); new XQueryRunConfigurationSerializer(configuration, xmlConfigurationAccessor, variablesAccessor, dataSourceAccessor, isDebugging, port).serialize(new FileWriter(serializedConfig)); return serializedConfig; } catch (Exception e) { throw new RuntimeException(e); } } private GeneralCommandLine createCommandLine() throws ExecutionException { final Project project = getEnvironment().getProject(); return createFromJavaParameters(getJavaParameters(), project); } private GeneralCommandLine createFromJavaParameters(final SimpleJavaParameters javaParameters, final Project project) throws CantRunException { return createFromJavaParameters(javaParameters, JdkUtil.useDynamicClasspath(project)); } private static GeneralCommandLine createFromJavaParameters(final SimpleJavaParameters javaParameters, final boolean forceDynamicClasspath) throws CantRunException { try { return ApplicationManager.getApplication().runReadAction(new Computable<GeneralCommandLine>() { public GeneralCommandLine compute() { try { final Sdk jdk = javaParameters.getJdk(); if (jdk == null) { throw new CantRunException(ExecutionBundle.message("run.configuration.error.no.jdk.specified")); } final SdkTypeId sdkType = jdk.getSdkType(); if (!(sdkType instanceof JavaSdkType)) { throw new CantRunException(ExecutionBundle.message("run.configuration.error.no.jdk.specified")); } final String exePath = ((JavaSdkType) sdkType).getVMExecutablePath(jdk); if (exePath == null) { throw new CantRunException(ExecutionBundle.message("run.configuration.cannot.find.vm.executable")); } if (javaParameters.getMainClass() == null && javaParameters.getJarPath() == null) { throw new CantRunException(ExecutionBundle.message("main.class.is.not.specified.error.message")); } return JdkUtil.setupJVMCommandLine(exePath, javaParameters, forceDynamicClasspath); } catch (CantRunException e) { throw new RuntimeException(e); } } }); } catch (RuntimeException e) { if (e.getCause() instanceof CantRunException) { throw (CantRunException) e.getCause(); } else { throw e; } } } private void configureConfiguration(SimpleJavaParameters parameters, XQueryRunConfiguration configuration) { ProgramParametersUtil.configureConfiguration(parameters, configuration); Project project = configuration.getProject(); Module module = getModule(configuration); ; String alternativeJrePath = configuration.getAlternativeJrePath(); if (alternativeJrePath != null) { configuration.setAlternativeJrePath(expandPath(alternativeJrePath, null, project)); } String vmParameters = configuration.getVMParameters(); if (vmParameters != null) { vmParameters = expandPath(vmParameters, module, project); for (Map.Entry<String, String> each : parameters.getEnv().entrySet()) { vmParameters = StringUtil.replace(vmParameters, "$" + each.getKey() + "$", each.getValue(), false); } } parameters.getVMParametersList().addParametersString(vmParameters); } private String expandPath(String path, Module module, Project project) { path = PathMacroManager.getInstance(project).expandPath(path); if (module != null) { path = PathMacroManager.getInstance(module).expandPath(path); } return path; } private Module getModule(CommonProgramRunConfigurationParameters configuration) { return configuration instanceof ModuleBasedConfiguration ? ((ModuleBasedConfiguration) configuration).getConfigurationModule().getModule() : null; } private Sdk getValidJdkToRunModule(final Module module) throws CantRunException { Sdk jdk = getJdkToRunModule(module); String currentRunningJavaHome = getCurrentRunningJavaHome(); if (jdk == null) { if (currentRunningJavaHome != null) { jdk = createAlternativeJdk(currentRunningJavaHome); } else { throw CantRunException.noJdkForModule(module); } } final VirtualFile homeDirectory = jdk.getHomeDirectory(); if (homeDirectory == null || !homeDirectory.isValid()) { throw CantRunException.jdkMisconfigured(jdk, module); } return jdk; } private Sdk getJdkToRunModule(Module module) { final Sdk moduleSdk = ModuleRootManager.getInstance(module).getSdk(); if (moduleSdk == null) { return null; } final Set<Sdk> sdksFromDependencies = new LinkedHashSet<>(); OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).runtimeOnly().recursively(); enumerator = enumerator.productionOnly(); enumerator.forEachModule(module1 -> { Sdk sdk = ModuleRootManager.getInstance(module1).getSdk(); if (sdk != null && sdk.getSdkType().equals(moduleSdk.getSdkType())) { sdksFromDependencies.add(sdk); } return true; }); return findLatestVersion(moduleSdk, sdksFromDependencies); } private String getCurrentRunningJavaHome() { String javaHomeOfCurrentProcess = System.getProperty("java.home"); if (javaHomeOfCurrentProcess != null && !javaHomeOfCurrentProcess.isEmpty()) { return javaHomeOfCurrentProcess; } return null; } private Sdk findLatestVersion(@NotNull Sdk mainSdk, @NotNull Set<Sdk> sdks) { Sdk result = mainSdk; for (Sdk sdk : sdks) { if (VersionComparatorUtil.compare(result.getVersionString(), sdk.getVersionString()) < 0) { result = sdk; } } return result; } private Sdk createAlternativeJdk(@NotNull String jreHome) throws CantRunException { final Sdk configuredJdk = ProjectJdkTable.getInstance().findJdk(jreHome); if (configuredJdk != null) { return configuredJdk; } if (!JdkUtil.checkForJre(jreHome) && !JdkUtil.checkForJdk(jreHome)) { throw new CantRunException(ExecutionBundle.message("jre.path.is.not.valid.jre.home.error.message", jreHome)); } final String versionString = SdkVersionUtil.detectJdkVersion(jreHome); final Sdk jdk = new SimpleJavaSdkType().createJdk(versionString != null ? versionString : "", jreHome); if (jdk == null) throw CantRunException.noJdkConfigured(); return jdk; } private VirtualFile[] jdkRoots(Sdk jdk) { return Arrays.stream(jdk.getRootProvider().getFiles(OrderRootType.CLASSES)).toArray(VirtualFile[]::new); } }