├── .gitignore ├── files ├── Test.properties ├── settings.png ├── context-menu.png ├── run-configuration.png └── Test.jmx ├── resources └── icons │ └── beaker.png ├── .idea ├── vcs.xml ├── .gitignore ├── modules.xml └── misc.xml ├── README.md ├── jmeter-idea-plugin.iml ├── src └── idea │ └── plugin │ └── jmeter │ ├── run │ ├── JmeterProgramRunner.java │ ├── JmeterConfigurationType.java │ ├── JmeterRunConfigurationEditor.java │ ├── PropertyTable.java │ ├── JmeterRunProfileState.java │ ├── JmeterRunConfigurationProducer.java │ ├── JmeterRunConfigurationForm.java │ ├── JmeterRunConfiguration.java │ └── JmeterRunConfigurationForm.form │ ├── JmeterFileType.java │ ├── settings │ ├── JmeterSettings.java │ ├── JmeterSettingsForm.java │ ├── JmeterSettingsConfigurable.java │ └── JmeterSettingsForm.form │ └── actions │ └── CreateJmeterFileAction.java ├── META-INF └── plugin.xml └── LICENSE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | -------------------------------------------------------------------------------- /files/Test.properties: -------------------------------------------------------------------------------- 1 | host=jmeter.apache.org -------------------------------------------------------------------------------- /files/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponomandr/jmeter-idea-plugin/HEAD/files/settings.png -------------------------------------------------------------------------------- /files/context-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponomandr/jmeter-idea-plugin/HEAD/files/context-menu.png -------------------------------------------------------------------------------- /files/run-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponomandr/jmeter-idea-plugin/HEAD/files/run-configuration.png -------------------------------------------------------------------------------- /resources/icons/beaker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponomandr/jmeter-idea-plugin/HEAD/resources/icons/beaker.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JMeter Idea Plugin 2 | Create run configurations and run [Apache JMeter](https://jmeter.apache.org/) tests from Intellij IDEA. 3 | Create and open `*.jmx` JMeter files. 4 | 5 | ![JMeter run config](https://plugins.jetbrains.com/files/7013/screenshot_13597.png) 6 | ![Create JMeter test file](https://plugins.jetbrains.com/files/7013/screenshot_13979.png) 7 | 8 | ## Install 9 | [Get from Marketplace](https://plugins.jetbrains.com/plugin/7013-jmeter-plugin) 10 | 11 | ## Development 12 | See [Пишем плагин к Intellij IDEA: Регистрация типа файла](https://habr.com/ru/post/149100/) article. 13 | -------------------------------------------------------------------------------- /jmeter-idea-plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterProgramRunner.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.execution.configurations.RunProfile; 4 | import com.intellij.execution.executors.DefaultRunExecutor; 5 | import com.intellij.execution.runners.DefaultProgramRunner; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class JmeterProgramRunner extends DefaultProgramRunner { 9 | 10 | @NotNull 11 | @Override 12 | public String getRunnerId() { 13 | return getClass().getName(); 14 | } 15 | 16 | @Override 17 | public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { 18 | return DefaultRunExecutor.EXECUTOR_ID.equals(executorId) && profile instanceof JmeterRunConfiguration; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/JmeterFileType.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter; 2 | 3 | import com.intellij.lang.xml.XMLLanguage; 4 | import com.intellij.openapi.fileTypes.LanguageFileType; 5 | import com.intellij.openapi.util.IconLoader; 6 | import com.intellij.openapi.vfs.CharsetToolkit; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import javax.swing.*; 11 | 12 | public class JmeterFileType extends LanguageFileType { 13 | public static final JmeterFileType INSTANCE = new JmeterFileType(); 14 | public static final Icon FILE_ICON = IconLoader.getIcon("/icons/beaker.png"); 15 | 16 | public JmeterFileType() { 17 | super(XMLLanguage.INSTANCE); 18 | } 19 | 20 | @NotNull 21 | @Override 22 | public String getName() { 23 | return "JMeter"; 24 | } 25 | 26 | @NotNull 27 | @Override 28 | public String getDescription() { 29 | return "JMeter files"; 30 | } 31 | 32 | @NotNull 33 | @Override 34 | public String getDefaultExtension() { 35 | return "jmx"; 36 | } 37 | 38 | @Override 39 | public Icon getIcon() { 40 | return FILE_ICON; 41 | } 42 | 43 | @Override 44 | public String getCharset(@NotNull VirtualFile virtualFile, @NotNull byte[] bytes) { 45 | return CharsetToolkit.UTF8; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/settings/JmeterSettings.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.settings; 2 | 3 | import com.intellij.ide.util.PropertiesComponent; 4 | import com.intellij.openapi.project.Project; 5 | 6 | import java.io.File; 7 | 8 | public class JmeterSettings { 9 | private static final String OVERRIDE_KEY = "jmeter.override"; 10 | private static final String JMETER_HOME_KEY = "jmeter.home"; 11 | 12 | private String jmeterHome; 13 | private boolean override; 14 | 15 | public static File getJmeterJar(Project project) { 16 | File binDir = new File(getJmeterHome(project), "bin"); 17 | return new File(binDir, "ApacheJMeter.jar"); 18 | } 19 | 20 | public String getJmeterHome() { 21 | return jmeterHome; 22 | } 23 | 24 | public void setJmeterHome(String jmeterHome) { 25 | this.jmeterHome = jmeterHome; 26 | } 27 | 28 | public boolean isOverride() { 29 | return override; 30 | } 31 | 32 | public void setOverride(boolean override) { 33 | this.override = override; 34 | } 35 | 36 | public static String getJmeterHome(Project project) { 37 | JmeterSettings settings = read(PropertiesComponent.getInstance(project)); 38 | return settings.isOverride() ? settings.getJmeterHome() : System.getenv("JMETER_HOME"); 39 | } 40 | 41 | public static JmeterSettings read(PropertiesComponent properties) { 42 | JmeterSettings settings = new JmeterSettings(); 43 | settings.setJmeterHome(properties.getValue(JMETER_HOME_KEY)); 44 | settings.setOverride(properties.getBoolean(OVERRIDE_KEY, false)); 45 | return settings; 46 | } 47 | 48 | public void save(PropertiesComponent properties) { 49 | properties.setValue(JMETER_HOME_KEY, jmeterHome); 50 | properties.setValue(OVERRIDE_KEY, String.valueOf(override)); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/settings/JmeterSettingsForm.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.settings; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooser; 4 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.TextFieldWithBrowseButton; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | 9 | import javax.swing.*; 10 | 11 | public class JmeterSettingsForm { 12 | private JPanel rootPanel; 13 | private TextFieldWithBrowseButton jmeterHome; 14 | private JCheckBox override; 15 | 16 | 17 | public JmeterSettingsForm(final Project project) { 18 | jmeterHome.addActionListener(e -> { 19 | VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, null); 20 | if (file != null) { 21 | jmeterHome.setText(file.getPath()); 22 | } 23 | }); 24 | 25 | override.addChangeListener(e -> jmeterHome.setEnabled(override.isSelected())); 26 | } 27 | 28 | public void setData(JmeterSettings data) { 29 | jmeterHome.setText(data.getJmeterHome()); 30 | override.setSelected(data.isOverride()); 31 | if (!data.isOverride()) { 32 | jmeterHome.setText(System.getenv("JMETER_HOME")); 33 | } 34 | } 35 | 36 | public void getData(JmeterSettings data) { 37 | data.setJmeterHome(jmeterHome.getText().trim()); 38 | data.setOverride(override.isSelected()); 39 | } 40 | 41 | public boolean isModified(JmeterSettings data) { 42 | if (!jmeterHome.getText().equals(data.getJmeterHome())) return true; 43 | if (override.isSelected() != data.isOverride()) return true; 44 | return false; 45 | } 46 | 47 | public JPanel getRootPanel() { 48 | return rootPanel; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterConfigurationType.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.execution.configurations.ConfigurationFactory; 4 | import com.intellij.execution.configurations.ConfigurationType; 5 | import com.intellij.execution.configurations.ConfigurationTypeUtil; 6 | import com.intellij.execution.configurations.RunConfiguration; 7 | import com.intellij.openapi.project.Project; 8 | import idea.plugin.jmeter.JmeterFileType; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import javax.swing.*; 12 | 13 | public class JmeterConfigurationType implements ConfigurationType { 14 | 15 | private ConfigurationFactory myConfigurationFactory; 16 | 17 | public JmeterConfigurationType() { 18 | myConfigurationFactory = new ConfigurationFactory(this) { 19 | @NotNull 20 | @Override 21 | public RunConfiguration createTemplateConfiguration(@NotNull Project project) { 22 | return new JmeterRunConfiguration(project, this); 23 | } 24 | }; 25 | } 26 | 27 | public static JmeterConfigurationType getInstance() { 28 | return ConfigurationTypeUtil.findConfigurationType(JmeterConfigurationType.class); 29 | } 30 | 31 | 32 | @NotNull 33 | @Override 34 | public String getDisplayName() { 35 | return "JMeter"; 36 | } 37 | 38 | @Override 39 | public String getConfigurationTypeDescription() { 40 | return null; 41 | } 42 | 43 | @Override 44 | public Icon getIcon() { 45 | return JmeterFileType.FILE_ICON; 46 | } 47 | 48 | @NotNull 49 | @Override 50 | public String getId() { 51 | return getClass().getName(); 52 | } 53 | 54 | @Override 55 | public ConfigurationFactory[] getConfigurationFactories() { 56 | return new ConfigurationFactory[]{myConfigurationFactory}; 57 | } 58 | 59 | public ConfigurationFactory getConfigurationFactory() { 60 | return myConfigurationFactory; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterRunConfigurationEditor.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SettingsEditor; 5 | import com.intellij.openapi.project.Project; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import javax.swing.*; 9 | 10 | public class JmeterRunConfigurationEditor extends SettingsEditor { 11 | 12 | private final Project project; 13 | private JmeterRunConfigurationForm form; 14 | 15 | public JmeterRunConfigurationEditor(Project project) { 16 | this.project = project; 17 | } 18 | 19 | @Override 20 | protected void resetEditorFrom(JmeterRunConfiguration runConfiguration) { 21 | form.setTestFile(runConfiguration.getTestFile()); 22 | form.setPropertyFile(runConfiguration.getPropertyFile()); 23 | form.setProperties(runConfiguration.getProperties()); 24 | form.setJvmParameters(runConfiguration.getJvmParameters()); 25 | form.setCustomParameters(runConfiguration.getCustomParameters()); 26 | form.setWorkingDirectory(runConfiguration.getWorkingDirectory()); 27 | } 28 | 29 | @Override 30 | protected void applyEditorTo(JmeterRunConfiguration runConfiguration) throws ConfigurationException { 31 | runConfiguration.setTestFile(form.getTestFile()); 32 | runConfiguration.setPropertyFile(form.getPropertyFile()); 33 | runConfiguration.setProperties(form.getProperties()); 34 | runConfiguration.setJvmParameters(form.getJvmParameters()); 35 | runConfiguration.setCustomParameters(form.getCustomParameters()); 36 | runConfiguration.setWorkingDirectory(form.getWorkingDirectory()); 37 | } 38 | 39 | @NotNull 40 | @Override 41 | protected JComponent createEditor() { 42 | form = new JmeterRunConfigurationForm(project); 43 | return form.getRootPanel(); 44 | } 45 | 46 | @Override 47 | protected void disposeEditor() { 48 | form = null; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/settings/JmeterSettingsConfigurable.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.settings; 2 | 3 | import com.intellij.ide.util.PropertiesComponent; 4 | import com.intellij.openapi.options.ConfigurationException; 5 | import com.intellij.openapi.options.SearchableConfigurable; 6 | import com.intellij.openapi.project.Project; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import javax.swing.*; 11 | 12 | public class JmeterSettingsConfigurable implements SearchableConfigurable { 13 | 14 | private final Project project; 15 | private final PropertiesComponent propertiesComponent; 16 | private JmeterSettingsForm settingsForm; 17 | private JmeterSettings settings; 18 | 19 | public JmeterSettingsConfigurable(Project project) { 20 | this.project = project; 21 | propertiesComponent = PropertiesComponent.getInstance(project); 22 | } 23 | 24 | @Nls 25 | @Override 26 | public String getDisplayName() { 27 | return "JMeter"; 28 | } 29 | 30 | @Override 31 | public String getHelpTopic() { 32 | return null; 33 | } 34 | 35 | @Override 36 | public JComponent createComponent() { 37 | settings = JmeterSettings.read(propertiesComponent); 38 | settingsForm = new JmeterSettingsForm(project); 39 | return settingsForm.getRootPanel(); 40 | } 41 | 42 | @Override 43 | public boolean isModified() { 44 | return settingsForm.isModified(settings); 45 | } 46 | 47 | @Override 48 | public void apply() throws ConfigurationException { 49 | settingsForm.getData(settings); 50 | settings.save(propertiesComponent); 51 | } 52 | 53 | @Override 54 | public void reset() { 55 | settingsForm.setData(settings); 56 | } 57 | 58 | @Override 59 | public void disposeUIResources() { 60 | settingsForm = null; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public String getId() { 66 | return getClass().getName(); 67 | } 68 | 69 | @Override 70 | public Runnable enableSearch(String s) { 71 | return null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/PropertyTable.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.openapi.actionSystem.ActionToolbarPosition; 4 | import com.intellij.ui.ToolbarDecorator; 5 | import com.intellij.ui.table.JBTable; 6 | import com.intellij.util.ui.EditableModel; 7 | 8 | import javax.swing.*; 9 | import javax.swing.table.DefaultTableModel; 10 | import java.awt.*; 11 | import java.util.LinkedHashMap; 12 | import java.util.Map; 13 | 14 | import static org.apache.commons.lang.StringUtils.isBlank; 15 | 16 | public class PropertyTable extends JPanel { 17 | private final PropertyTableModel model; 18 | 19 | public PropertyTable() { 20 | super(new BorderLayout()); 21 | model = new PropertyTableModel(); 22 | JBTable table = new JBTable(model); 23 | ToolbarDecorator decorator = ToolbarDecorator.createDecorator(table).setToolbarPosition(ActionToolbarPosition.RIGHT); 24 | add(decorator.createPanel(), BorderLayout.CENTER); 25 | } 26 | 27 | public LinkedHashMap getProperties() { 28 | LinkedHashMap map = new LinkedHashMap<>(); 29 | for (int row = 0; row < model.getRowCount(); row++) { 30 | Object key = model.getValueAt(row, 0); 31 | if (key == null || isBlank(key.toString())) continue; 32 | 33 | Object value = model.getValueAt(row, 1); 34 | map.put(key.toString(), value == null ? "" : value.toString().trim()); 35 | } 36 | return map; 37 | } 38 | 39 | public void setProperties(Map map) { 40 | model.setRowCount(map.size()); 41 | int row = 0; 42 | for (Map.Entry entry : map.entrySet()) { 43 | model.setValueAt(entry.getKey(), row, 0); 44 | model.setValueAt(entry.getValue(), row, 1); 45 | row++; 46 | } 47 | } 48 | 49 | private static class PropertyTableModel extends DefaultTableModel implements EditableModel { 50 | public PropertyTableModel() { 51 | super(new String[]{"Property", "Value"}, 1); 52 | } 53 | 54 | @Override 55 | public void addRow() { 56 | addRow(new Object[]{"", ""}); 57 | } 58 | 59 | @Override 60 | public void exchangeRows(int oldIndex, int newIndex) { 61 | moveRow(oldIndex, oldIndex, newIndex); 62 | } 63 | 64 | @Override 65 | public boolean canExchangeRows(int oldIndex, int newIndex) { 66 | return true; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterRunProfileState.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.execution.ExecutionException; 4 | import com.intellij.execution.configurations.JavaCommandLineState; 5 | import com.intellij.execution.configurations.JavaParameters; 6 | import com.intellij.execution.configurations.ParametersList; 7 | import com.intellij.execution.filters.TextConsoleBuilderImpl; 8 | import com.intellij.execution.runners.ExecutionEnvironment; 9 | import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; 10 | import idea.plugin.jmeter.settings.JmeterSettings; 11 | 12 | import java.util.Map; 13 | 14 | import static org.apache.commons.lang.StringUtils.isBlank; 15 | 16 | class JmeterRunProfileState extends JavaCommandLineState { 17 | private final JmeterRunConfiguration runConfiguration; 18 | private final ExecutionEnvironment executionEnvironment; 19 | 20 | public JmeterRunProfileState(ExecutionEnvironment executionEnvironment) { 21 | super(executionEnvironment); 22 | this.runConfiguration = (JmeterRunConfiguration) executionEnvironment.getRunProfile(); 23 | this.executionEnvironment = executionEnvironment; 24 | setConsoleBuilder(new TextConsoleBuilderImpl(executionEnvironment.getProject())); 25 | } 26 | 27 | @Override 28 | protected JavaParameters createJavaParameters() throws ExecutionException { 29 | JavaParameters parameters = new JavaParameters(); 30 | parameters.setJdk(JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk()); 31 | parameters.setMainClass("org.apache.jmeter.NewDriver"); 32 | parameters.getClassPath().add(JmeterSettings.getJmeterJar(executionEnvironment.getProject())); 33 | if (!isBlank(runConfiguration.getWorkingDirectory())) { 34 | parameters.setWorkingDirectory(runConfiguration.getWorkingDirectory()); 35 | } 36 | 37 | ParametersList programParameters = parameters.getProgramParametersList(); 38 | programParameters.add("--testfile", runConfiguration.getTestFile()); 39 | 40 | if (!isBlank(runConfiguration.getPropertyFile())) { 41 | programParameters.add("--addprop", runConfiguration.getPropertyFile()); 42 | } 43 | 44 | for (Map.Entry entry : runConfiguration.getProperties().entrySet()) { 45 | programParameters.add("-J", entry.getKey() + "=" + entry.getValue()); 46 | } 47 | parameters.getVMParametersList().addParametersString(runConfiguration.getJvmParameters()); 48 | programParameters.addParametersString(runConfiguration.getCustomParameters()); 49 | return parameters; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterRunConfigurationProducer.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.execution.Location; 4 | import com.intellij.execution.actions.ConfigurationContext; 5 | import com.intellij.execution.actions.RunConfigurationProducer; 6 | import com.intellij.openapi.util.Ref; 7 | import com.intellij.openapi.util.io.FileUtil; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.PsiElement; 10 | import idea.plugin.jmeter.JmeterFileType; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class JmeterRunConfigurationProducer extends RunConfigurationProducer { 14 | 15 | public JmeterRunConfigurationProducer() { 16 | super(JmeterConfigurationType.getInstance()); 17 | } 18 | 19 | @Override 20 | protected boolean setupConfigurationFromContext(@NotNull JmeterRunConfiguration runConfiguration, ConfigurationContext context, @NotNull Ref psiElementRef) { 21 | Location location = context.getLocation(); 22 | if (location == null) return false; 23 | 24 | VirtualFile testFile = location.getVirtualFile(); 25 | 26 | if (testFile == null || !JmeterFileType.INSTANCE.equals(testFile.getFileType())) { 27 | return false; 28 | } 29 | 30 | runConfiguration.setTestFile(testFile.getPath()); 31 | runConfiguration.setName(testFile.getName()); 32 | 33 | VirtualFile propertyFile = testFile.getParent().findChild("jmeter.properties"); 34 | if (propertyFile != null) { 35 | runConfiguration.setPropertyFile(propertyFile.getPath()); 36 | } 37 | 38 | String customParameters = ""; 39 | 40 | VirtualFile systemPropertyFile = testFile.getParent().findChild("system.properties"); 41 | if (systemPropertyFile != null) { 42 | customParameters += "--systemPropertyFile " + systemPropertyFile.getPath() + " "; 43 | } 44 | 45 | VirtualFile userPropertyFile = testFile.getParent().findChild("user.properties"); 46 | if (userPropertyFile != null) { 47 | customParameters += "--addprop " + userPropertyFile.getPath() + " "; 48 | } 49 | 50 | VirtualFile testPropertyFile = testFile.getParent().findChild(testFile.getNameWithoutExtension() + ".properties"); 51 | if (testPropertyFile != null) { 52 | customParameters += "--addprop " + testPropertyFile.getPath() + " "; 53 | } 54 | 55 | runConfiguration.setCustomParameters(customParameters.trim()); 56 | return true; 57 | } 58 | 59 | @Override 60 | public boolean isConfigurationFromContext(@NotNull JmeterRunConfiguration runConfiguration, ConfigurationContext context) { 61 | Location location = context.getLocation(); 62 | if (location == null) return false; 63 | 64 | VirtualFile file = location.getVirtualFile(); 65 | if (file == null) return false; 66 | 67 | return FileUtil.toSystemIndependentName(file.getPath()).equals(FileUtil.toSystemIndependentName(runConfiguration.getTestFile())); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | JMeter plugin 3 | Create run configurations and run JMeter tests

5 |

Source code on GitHub

6 | ]]>
7 | 1.6.1 8 | Andrey Ponomarev 9 | Tools Integration 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Changes in 1.7 41 |
    42 |
  • Upgraded to Intellij IDEA 2020.3
  • 43 |
44 |

Changes in 1.6

45 |
    46 |
  • Upgraded to Intellij IDEA 13.1
  • 47 |
  • Added "New JMeter File" action
  • 48 |
49 |

Changes in 1.5

50 |
    51 |
  • Upgraded to Intellij IDEA 12.1
  • 52 |
53 |

Changes in 1.4

54 |
    55 |
  • New run configuration parameter: JVM parameters
  • 56 |
  • Syntax highlighting for .jmx files
  • 57 |
58 |

Changes in 1.3

59 |
    60 |
  • Add jmeter.properties, system.properties, user.properties files to a new run configuration if they exist in the test file folder
  • 61 |
62 |

Changes in 1.2

63 |
    64 |
  • New run configuration parameter: working directory
  • 65 |
66 |

Changes in 1.1

67 |
    68 |
  • Added "Run", "Create" and "Select" actions to context menu similar to JUnit tests.
  • 69 |
70 | ]]> 71 |
72 | 73 |
-------------------------------------------------------------------------------- /src/idea/plugin/jmeter/settings/JmeterSettingsForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterRunConfigurationForm.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooser; 4 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.TextFieldWithBrowseButton; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | 9 | import javax.swing.*; 10 | import java.util.LinkedHashMap; 11 | import java.util.Map; 12 | 13 | public class JmeterRunConfigurationForm { 14 | private JPanel rootPanel; 15 | private TextFieldWithBrowseButton testFile; 16 | private TextFieldWithBrowseButton propertyFile; 17 | private PropertyTable propertyTable; 18 | private JTextField jvmParameters; 19 | private JTextField customParameters; 20 | private TextFieldWithBrowseButton workingDirectory; 21 | 22 | public JmeterRunConfigurationForm(final Project project) { 23 | testFile.addActionListener(e -> { 24 | VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleLocalFileDescriptor(), project, null); 25 | if (file != null) { 26 | testFile.setText(file.getPath()); 27 | } 28 | }); 29 | 30 | propertyFile.addActionListener(e -> { 31 | VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleLocalFileDescriptor(), project, null); 32 | if (file != null) { 33 | propertyFile.setText(file.getPath()); 34 | } 35 | }); 36 | 37 | workingDirectory.addActionListener(e -> { 38 | VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, null); 39 | if (file != null) { 40 | workingDirectory.setText(file.getPath()); 41 | } 42 | }); 43 | } 44 | 45 | public JPanel getRootPanel() { 46 | return rootPanel; 47 | } 48 | 49 | public String getTestFile() { 50 | return testFile.getText(); 51 | } 52 | 53 | public void setTestFile(String testFile) { 54 | this.testFile.setText(testFile); 55 | } 56 | 57 | public String getPropertyFile() { 58 | return propertyFile.getText(); 59 | } 60 | 61 | public void setPropertyFile(String propertyFile) { 62 | this.propertyFile.setText(propertyFile); 63 | } 64 | 65 | public LinkedHashMap getProperties() { 66 | return propertyTable.getProperties(); 67 | } 68 | 69 | public void setProperties(Map properties) { 70 | propertyTable.setProperties(properties); 71 | } 72 | 73 | public String getJvmParameters() { 74 | return jvmParameters.getText(); 75 | } 76 | 77 | public void setJvmParameters(String jvmParameters) { 78 | this.jvmParameters.setText(jvmParameters); 79 | } 80 | 81 | public String getCustomParameters() { 82 | return customParameters.getText(); 83 | } 84 | 85 | public void setCustomParameters(String customParameters) { 86 | this.customParameters.setText(customParameters); 87 | } 88 | 89 | public String getWorkingDirectory() { 90 | return workingDirectory.getText(); 91 | } 92 | 93 | public void setWorkingDirectory(String workingDirectory) { 94 | this.workingDirectory.setText(workingDirectory); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/actions/CreateJmeterFileAction.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.actions; 2 | 3 | import com.intellij.ide.actions.CreateElementActionBase; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.Messages; 6 | import com.intellij.psi.PsiDirectory; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.psi.PsiFile; 9 | import com.intellij.psi.PsiFileFactory; 10 | import idea.plugin.jmeter.JmeterFileType; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class CreateJmeterFileAction extends CreateElementActionBase { 14 | 15 | public CreateJmeterFileAction() { 16 | super("Create JMeter File", JmeterFileType.INSTANCE.getDescription(), JmeterFileType.FILE_ICON); 17 | } 18 | 19 | @NotNull 20 | @Override 21 | protected PsiElement[] invokeDialog(Project project, PsiDirectory directory) { 22 | CreateElementActionBase.MyInputValidator validator = new CreateElementActionBase.MyInputValidator(project, directory); 23 | Messages.showInputDialog(project, "Enter a new file name:", "New JMeter File", Messages.getQuestionIcon(), "", validator); 24 | return validator.getCreatedElements(); 25 | } 26 | 27 | @NotNull 28 | @Override 29 | protected PsiElement[] create(String newName, PsiDirectory directory) { 30 | String ext = '.' + JmeterFileType.INSTANCE.getDefaultExtension(); 31 | String fileName = newName.endsWith(ext) ? newName : newName + ext; 32 | String testPlanName = newName.endsWith(ext) ? newName.substring(0, newName.length() - ext.length()) : newName; 33 | PsiFile file = PsiFileFactory.getInstance(directory.getProject()).createFileFromText(fileName, JmeterFileType.INSTANCE, 34 | "\n" + 35 | "\n" + 36 | " \n" + 37 | " \n" + 38 | " \n" + 39 | " false\n" + 40 | " false\n" + 41 | " \n" + 42 | " \n" + 43 | " \n" + 44 | " \n" + 45 | " \n" + 46 | " \n" + 47 | " \n" + 48 | "\n"); 49 | return new PsiElement[]{directory.add(file)}; 50 | } 51 | 52 | @Override 53 | protected String getErrorTitle() { 54 | return "Cannot create JMeter File"; 55 | } 56 | 57 | @Override 58 | protected String getCommandName() { 59 | return "Create JMeter File"; 60 | } 61 | 62 | @Override 63 | protected String getActionName(PsiDirectory psiDirectory, String s) { 64 | return "JMeter File"; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterRunConfiguration.java: -------------------------------------------------------------------------------- 1 | package idea.plugin.jmeter.run; 2 | 3 | import com.intellij.execution.ExecutionException; 4 | import com.intellij.execution.Executor; 5 | import com.intellij.execution.configurations.*; 6 | import com.intellij.execution.runners.ExecutionEnvironment; 7 | import com.intellij.openapi.options.SettingsEditor; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.util.InvalidDataException; 10 | import com.intellij.openapi.util.JDOMExternalizer; 11 | import com.intellij.openapi.util.WriteExternalException; 12 | import idea.plugin.jmeter.settings.JmeterSettings; 13 | import org.jdom.Element; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.io.File; 17 | import java.util.LinkedHashMap; 18 | 19 | import static org.apache.commons.lang.StringUtils.isBlank; 20 | 21 | public class JmeterRunConfiguration extends RunConfigurationBase implements LocatableConfiguration { 22 | private String testFile; 23 | private String propertyFile; 24 | private LinkedHashMap properties = new LinkedHashMap<>(); 25 | private String jvmParameters; 26 | private String customParameters; 27 | private String workingDirectory; 28 | 29 | public JmeterRunConfiguration(Project project, ConfigurationFactory configurationFactory) { 30 | super(project, configurationFactory, ""); 31 | } 32 | 33 | @NotNull 34 | @Override 35 | public SettingsEditor getConfigurationEditor() { 36 | return new JmeterRunConfigurationEditor(getProject()); 37 | } 38 | 39 | @Override 40 | public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException { 41 | return new JmeterRunProfileState(executionEnvironment); 42 | } 43 | 44 | @Override 45 | public void checkConfiguration() throws RuntimeConfigurationException { 46 | if (testFile == null || !new File(testFile).exists()) { 47 | throw new RuntimeConfigurationException("Test file not found"); 48 | } 49 | 50 | if (!isBlank(propertyFile) && !new File(propertyFile).exists()) { 51 | throw new RuntimeConfigurationException("Properties file not found"); 52 | } 53 | 54 | if (!JmeterSettings.getJmeterJar(getProject()).exists()) { 55 | throw new RuntimeConfigurationException("JMeter not found"); 56 | } 57 | } 58 | 59 | @Override 60 | public void writeExternal(@NotNull Element element) throws WriteExternalException { 61 | super.writeExternal(element); 62 | JDOMExternalizer.write(element, "testFile", testFile); 63 | JDOMExternalizer.write(element, "propertyFile", propertyFile); 64 | JDOMExternalizer.write(element, "jvmParameters", jvmParameters); 65 | JDOMExternalizer.write(element, "customParameters", customParameters); 66 | JDOMExternalizer.write(element, "workingDirectory", workingDirectory); 67 | JDOMExternalizer.writeMap(element, properties, "properties", "property"); 68 | } 69 | 70 | @Override 71 | public void readExternal(@NotNull Element element) throws InvalidDataException { 72 | super.readExternal(element); 73 | testFile = JDOMExternalizer.readString(element, "testFile"); 74 | propertyFile = JDOMExternalizer.readString(element, "propertyFile"); 75 | jvmParameters = JDOMExternalizer.readString(element, "jvmParameters"); 76 | customParameters = JDOMExternalizer.readString(element, "customParameters"); 77 | workingDirectory = JDOMExternalizer.readString(element, "workingDirectory"); 78 | 79 | LinkedHashMap properties = this.properties; 80 | JDOMExternalizer.readMap(element, properties, "properties", "property"); 81 | this.properties = properties; 82 | } 83 | 84 | public String getTestFile() { 85 | return testFile; 86 | } 87 | 88 | public void setTestFile(String testFile) { 89 | this.testFile = testFile; 90 | } 91 | 92 | public String getPropertyFile() { 93 | return propertyFile; 94 | } 95 | 96 | public void setPropertyFile(String propertyFile) { 97 | this.propertyFile = propertyFile; 98 | } 99 | 100 | public LinkedHashMap getProperties() { 101 | return properties; 102 | } 103 | 104 | public void setProperties(LinkedHashMap properties) { 105 | this.properties = properties; 106 | } 107 | 108 | public String getJvmParameters() { 109 | return jvmParameters; 110 | } 111 | 112 | public void setJvmParameters(String jvmParameters) { 113 | this.jvmParameters = jvmParameters; 114 | } 115 | 116 | public String getCustomParameters() { 117 | return customParameters; 118 | } 119 | 120 | public void setCustomParameters(String customParameters) { 121 | this.customParameters = customParameters; 122 | } 123 | 124 | public String getWorkingDirectory() { 125 | return workingDirectory; 126 | } 127 | 128 | public void setWorkingDirectory(String workingDirectory) { 129 | this.workingDirectory = workingDirectory; 130 | } 131 | 132 | @Override 133 | public boolean isGeneratedName() { 134 | return false; 135 | } 136 | 137 | @Override 138 | public String suggestedName() { 139 | return getName(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /files/Test.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | false 10 | false 11 | Sample test for demonstrating jmeter-idea-plugin 12 | 13 | 14 | 15 | false 16 | 17 | saveConfig 18 | 19 | 20 | true 21 | true 22 | true 23 | 24 | true 25 | true 26 | true 27 | true 28 | false 29 | true 30 | true 31 | false 32 | false 33 | true 34 | false 35 | false 36 | false 37 | false 38 | false 39 | 0 40 | true 41 | 42 | 43 | 44 | 45 | 46 | 47 | 1143889321000 48 | 49 | 50 | 1 51 | false 52 | 53 | false 54 | 1 55 | 56 | 1143889321000 57 | stoptest 58 | 1 59 | 60 | 61 | 62 | 63 | 64 | 65 | ${__P(host, apache.org)} 66 | 67 | 68 | 69 | 70 | 71 | / 72 | GET 73 | true 74 | false 75 | false 76 | false 77 | true 78 | false 79 | 80 | 81 | 82 | 83 | 84 | Apache Software Foundation 85 | 86 | Assertion.response_data 87 | false 88 | 2 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/idea/plugin/jmeter/run/JmeterRunConfigurationForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
128 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------