diff --git a/.idea/misc.xml b/.idea/misc.xml index 303054c..ed58562 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,19 +1,6 @@ - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml index a888be8..a05fbb5 100644 --- a/META-INF/plugin.xml +++ b/META-INF/plugin.xml @@ -1,7 +1,7 @@ org.turbanov.execution.cmd RunInCmd - 1.4 + 1.5 Turbanov Andrey --> 1.5 +
    +
  • + Added $freePort macros available in "To add" options. Plugin will replace it with some free port.
    + This is very useful when you want to debug application launched via this plugin.
    + Just add
    -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$freePort
    to "To add VM options". +
  • +

1.4

  • Fix run inside 2016.x IDEA terminal
  • diff --git a/runInCmdPlugin.iml b/runInCmdPlugin.iml index fd29e51..2ebb730 100644 --- a/runInCmdPlugin.iml +++ b/runInCmdPlugin.iml @@ -1,7 +1,7 @@ - + diff --git a/src/org/turbanov/execution/cmd/InCmdConfigurable.java b/src/org/turbanov/execution/cmd/InCmdConfigurable.java index 7326c12..060641f 100644 --- a/src/org/turbanov/execution/cmd/InCmdConfigurable.java +++ b/src/org/turbanov/execution/cmd/InCmdConfigurable.java @@ -10,6 +10,7 @@ import javax.swing.*; import java.awt.*; +import java.util.Objects; /** * @author Andrey Turbanov @@ -23,6 +24,8 @@ public class InCmdConfigurable implements Configurable { private JTextArea toAddProgramOptions; private JTextArea toRemoveProgramOptions; private JCheckBox runInTerminal; + private SpinnerNumberModel freePortSpinnerModel; + private JCheckBox findFreePort; public InCmdConfigurable(Project project) { myProject = project; @@ -59,19 +62,32 @@ public JComponent createComponent() { runInTerminal = new JCheckBox("Run inside IDEA Terminal", myState.isRunInsideTerminal); + Box bottomBox = Box.createHorizontalBox(); + freePortSpinnerModel = new SpinnerNumberModel(60000, 1024, 65535, 1); + JSpinner freePortSpinner = new JSpinner(freePortSpinnerModel); + findFreePort = new JCheckBox("Try to find free port"); + findFreePort.setToolTipText("At start plugin will try to find free port\n" + + "This port will be available as $freePort macros in program/VM options"); + bottomBox.add(findFreePort); + bottomBox.add(LabeledComponent.create(freePortSpinner, "Start value to check")); + JPanel result = new JPanel(new BorderLayout()); result.add(runInTerminal, BorderLayout.PAGE_START); result.add(mainBox, BorderLayout.CENTER); + result.add(bottomBox, BorderLayout.PAGE_END); return result; } @Override public boolean isModified() { - return !toAddVmOptions.getText().equals(myState.toAddVmOptions) - || !toRemoveVmOptions.getText().equals(myState.toRemoveVmOptions) - || !toAddProgramOptions.getText().equals(myState.toAddProgramOptions) - || !toRemoveProgramOptions.getText().equals(myState.toRemoveProgramOptions) - || runInTerminal.isSelected() != myState.isRunInsideTerminal; + boolean simpleModified = !toAddVmOptions.getText().equals(myState.toAddVmOptions) + || !toRemoveVmOptions.getText().equals(myState.toRemoveVmOptions) + || !toAddProgramOptions.getText().equals(myState.toAddProgramOptions) + || !toRemoveProgramOptions.getText().equals(myState.toRemoveProgramOptions) + || runInTerminal.isSelected() != myState.isRunInsideTerminal; + if (simpleModified) return true; + Integer currentValue = findFreePort.isSelected() ? freePortSpinnerModel.getNumber().intValue() : null; + return !Objects.equals(currentValue, myState.startPort); } @Override @@ -82,6 +98,7 @@ public void apply() throws ConfigurationException { state.toAddProgramOptions = toAddProgramOptions.getText().trim(); state.toRemoveProgramOptions = toRemoveProgramOptions.getText().trim(); state.isRunInsideTerminal = runInTerminal.isSelected(); + state.startPort = findFreePort.isSelected() ? freePortSpinnerModel.getNumber().intValue() : null; ServiceManager.getService(myProject, OptionsPatchConfiguration.class).loadState(state); } @@ -92,6 +109,12 @@ public void reset() { toAddProgramOptions.setText(myState.toAddProgramOptions); toRemoveProgramOptions.setText(myState.toRemoveProgramOptions); runInTerminal.setSelected(myState.isRunInsideTerminal); + if (myState.startPort == null) { + findFreePort.setSelected(false); + } else { + findFreePort.setSelected(true); + freePortSpinnerModel.setValue(myState.startPort); + } } @Override diff --git a/src/org/turbanov/execution/cmd/InCmdRunner.java b/src/org/turbanov/execution/cmd/InCmdRunner.java index 98ff786..d5cab38 100644 --- a/src/org/turbanov/execution/cmd/InCmdRunner.java +++ b/src/org/turbanov/execution/cmd/InCmdRunner.java @@ -29,6 +29,7 @@ import java.io.File; import java.io.IOException; +import java.net.ServerSocket; import java.nio.charset.Charset; import java.util.HashSet; import java.util.List; @@ -70,8 +71,8 @@ protected RunContentDescriptor doExecute(@NotNull RunProfileState runProfileStat LOG.info("Old command line: " + oldCommandLine); OptionsPatchConfiguration options = ServiceManager.getService(environment.getProject(), OptionsPatchConfiguration.class); - patchParameterList(javaParameters.getVMParametersList(), options.toAddVmOptions, options.toRemoveVmOptions); - patchParameterList(javaParameters.getProgramParametersList(), options.toAddProgramOptions, options.toRemoveProgramOptions); + patchParameterList(javaParameters.getVMParametersList(), options.toAddVmOptions, options.toRemoveVmOptions, options.startPort); + patchParameterList(javaParameters.getProgramParametersList(), options.toAddProgramOptions, options.toRemoveProgramOptions, options.startPort); String workingDirectory = state.getJavaParameters().getWorkingDirectory(); @@ -148,8 +149,14 @@ private static void clear(PathsList classPath) { } } - private static void patchParameterList(ParametersList parametersList, String toAdd, String toRemove) { + private static void patchParameterList(ParametersList parametersList, String toAdd, String toRemove, Integer startPort) { if (!toAdd.isEmpty()) { + if (startPort != null && toAdd.contains("$freePort")) { + Integer freePort = findFreePort(startPort); + if (freePort != null) { + toAdd = toAdd.replace("$freePort", freePort.toString()); + } + } String[] toAddParams = ParametersList.parse(toAdd); for (String toAddParam : toAddParams) { if (!parametersList.hasParameter(toAddParam)) { @@ -169,4 +176,25 @@ private static void patchParameterList(ParametersList parametersList, String toA } } } + + //Adapted from org.jetbrains.jps.incremental.java.JavaBuilder.findFreePort() + private static Integer findFreePort(int startFrom) { + for (int i = 0; i < 100; i++) { + try { + int tryPort = startFrom + i; + if (tryPort < 0) { + return null; + } + try (ServerSocket serverSocket = new ServerSocket(tryPort)) { + // calling close() immediately after opening socket may result that socket is not closed + synchronized (serverSocket) { + serverSocket.wait(1); + } + } + return tryPort; + } catch (IOException | InterruptedException ignored) { + } + } + return null; + } } diff --git a/src/org/turbanov/execution/cmd/OptionsPatchConfiguration.java b/src/org/turbanov/execution/cmd/OptionsPatchConfiguration.java index be6cc4f..e90deaa 100644 --- a/src/org/turbanov/execution/cmd/OptionsPatchConfiguration.java +++ b/src/org/turbanov/execution/cmd/OptionsPatchConfiguration.java @@ -17,6 +17,7 @@ public class OptionsPatchConfiguration implements PersistentStateComponent