How to persist/read-back Run Configuration parameters in Intellij plugin - intellij-idea

I'm making a basic IntelliJ plugin that lets a user define Run Configuration (following the tutorial at [1]), and use said Run Configurations to execute the file open in the editor on a remote server.
My Run Configuration is simple (3 text fields), and I have it all working, however, after editing the Run Configuration, and click "Apply" or "OK" after changing values, the entered values are lost.
What is the correct way to persist and read-back values (both when the Run Configuration is re-opened as well as when the Run Configuration's Runner invoked)? It looks like I could try to create a custom persistence using [2], however, it seems like the Plugin framework should have a way to handle this already or at least hooks for when Apply/OK is pressed.
[1] https://www.jetbrains.org/intellij/sdk/docs/tutorials/run_configurations.html
[2] https://www.jetbrains.org/intellij/sdk/docs/basics/persisting_state_of_components.html

Hopefully, this post is a bit more clear to those new to IntelliJ plugin development and illustrates how persisting/loading Run Configurations can be achieved. Please read through the code comments as this is where much of the explanation takes place.
Also now that SettingsEditorImpl is my custom implementation of the SettingsEditor abstract class, and likewise, RunConfigurationImpl is my custom implementation of the RunConfigiration abstract class.
The first thing to do is to expose the form fields via custom getters on your SettingsEditorImpl (ie. getHost())
public class SettingsEditorImpl extends SettingsEditor<RunConfigurationImpl> {
private JPanel configurationPanel; // This is the outer-most JPanel
private JTextField hostJTextField;
public SettingsEditorImpl() {
super();
}
#NotNull
#Override
protected JComponent createEditor() {
return configurationPanel;
}
/* Gets the Form fields value */
private String getHost() {
return hostJTextField.getText();
}
/* Copy value FROM your custom runConfiguration back INTO the Form UI; This is to load previously saved values into the Form when it's opened. */
#Override
protected void resetEditorFrom(RunConfigurationImpl runConfiguration) {
hostJTextField.setText(StringUtils.defaultIfBlank(runConfiguration.getHost(), RUN_CONFIGURATION_HOST_DEFAULT));
}
/* Sync the value from the Form UI INTO the RunConfiguration which is what the rest of your code will interact with. This requires a way to set this value on your custom RunConfiguration, ie. RunConfigurationImpl##setHost(host) */
#Override
protected void applyEditorTo(RunConfigurationImpl runConfiguration) throws ConfigurationException {
runConfiguration.setHost(getHost());
}
}
So now, the custom SettingsEditor, which backs the Form UI, is set up to Sync field values In and Out of itself. Remember, the custom RunConfiguration is what is going to actually represent this configuration; the SettingsEditor implementation just represents the FORM (a subtle difference, but important).
Now we need a custom RunConfiguration ...
/* Annotate the class with #State and #Storage, which is used to define how this RunConfiguration's data will be persisted/loaded. */
#State(
name = Constants.PLUGIN_NAME,
storages = {#Storage(Constants.PLUGIN_NAME + "__run-configuration.xml")}
)
public class RunConfigurationImpl extends RunConfigurationBase {
// Its good to 'namespace' keys to your component;
public static final String KEY_HOST = Constants.PLUGIN_NAME + ".host";
private String host;
public RunConfigurationImpl(Project project, ConfigurationFactory factory, String name) {
super(project, factory, name);
}
/* Return an instances of the custom SettingsEditor ... see class defined above */
#NotNull
#Override
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new SettingsEditorImpl();
}
/* Return null, else we'll get a Startup/Connection tab in our Run Configuration UI in IntelliJ */
#Nullable
#Override
public SettingsEditor<ConfigurationPerRunnerSettings> getRunnerSettingsEditor(ProgramRunner runner) {
return null;
}
/* This is a pretty cool method. Every time SettingsEditor#applyEditorTo() is changed the values in this class, this method is run and can check/validate any fields! If RuntimeConfigurationException is thrown, the exceptions message is shown at the bottom of the Run Configuration UI in IntelliJ! */
#Override
public void checkConfiguration() throws RuntimeConfigurationException {
if (!StringUtils.startsWithAny(getHost(), "http://", "https://")) {
throw new RuntimeConfigurationException("Invalid host");
}
}
#Nullable
#Override
public RunProfileState getState(#NotNull Executor executor, #NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
return null;
}
/* This READS any prior persisted configuration from the State/Storage defined by this classes annotations ... see above.
You must manually read and populate the fields using JDOMExternalizerUtil.readField(..).
This method is invoked at the "right time" by the plugin framework. You dont need to call this.
*/
#Override
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
host = JDOMExternalizerUtil.readField(element, KEY_HOST);
}
/* This WRITES/persists configurations TO the State/Storage defined by this classes annotations ... see above.
You must manually read and populate the fields using JDOMExternalizerUtil.writeField(..).
This method is invoked at the "right time" by the plugin framework. You dont need to call this.
*/
#Override
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
JDOMExternalizerUtil.writeField(element, KEY_HOST, host);
}
/* This method is what's used by the rest of the plugin code to access the configured 'host' value. The host field (variable) is written by
1. when writeExternal(..) loads a value from a persisted config.
2. when SettingsEditor#applyEditorTo(..) is called when the Form itself changes.
*/
public String getHost() {
return host;
}
/* This method sets the value, and is primarily used by the custom SettingEditor's SettingsEditor#applyEditorTo(..) method call */
public void setHost(String host) {
this.host = host;
}
}
To read these configuration values elsewhere, say for example a custom ProgramRunner, you would do something like:
final RunConfigurationImpl runConfiguration = (RunConfigurationImpl) executionEnvironment.getRunnerAndConfigurationSettings().getConfiguration();
runConfiguration.getHost(); // Returns the configured host value

See com.intellij.execution.configurations.RunConfigurationBase#readExternal as well as com.intellij.execution.configurations.RunConfigurationBase#loadState and com.intellij.execution.configurations.RunConfigurationBase#writeExternal

Related

How to Take Screenshot when TestNG Assert fails?

String Actualvalue= d.findElement(By.xpath("//[#id=\"wrapper\"]/main/div[2]/div/div[1]/div/div[1]/div[2]/div/table/tbody/tr[1]/td[1]/a")).getText();
Assert.assertEquals(Actualvalue, "jumlga");
captureScreen(d, "Fail");
The assert should not be put before your capture screen. Because it will immediately shutdown the test process so your code
captureScreen(d, "Fail");
will be not reachable
This is how i usually do:
boolean result = false;
try {
// do stuff here
result = true;
} catch(Exception_class_Name ex) {
// code to handle error and capture screen shot
captureScreen(d, "Fail");
}
# then using assert
Assert.assertEquals(result, true);
1.
A good solution will be is to use a report framework like allure-reports.
Read here:allure-reports
2.
We don't our tests to be ugly by adding try catch in every test so we will use Listeners which are using an annotations system to "Listen" to our tests and act accordingly.
Example:
public class listeners extends commonOps implements ITestListener {
public void onTestFailure(ITestResult iTestResult) {
System.out.println("------------------ Starting Test: " + iTestResult.getName() + " Failed ------------------");
if (platform.equalsIgnoreCase("web"))
saveScreenshot();
}
}
Please note I only used the relevant method to your question and I suggest you read here:
TestNG Listeners
Now we will want to take a screenshot built in method by allure-reports every time a test fails so will add this method inside our listeners class
Example:
#Attachment(value = "Page Screen-Shot", type = "image/png")
public byte[] saveScreenshot(){
return ((TakesScreenshot)driver).getScreenshotAs(OutputType.BYTES);
}
Test example
#Listeners(listeners.class)
public class myTest extends commonOps {
#Test(description = "Test01: Add numbers and verify")
#Description("Test Description: Using Allure reports annotations")
public void test01_myFirstTest(){
Assert.assertEquals(result, true)
}
}
Note we're using at the beginning of the class an annotation of #Listeners(listeners.class) which allows our listeners to listen to our test, please mind the (listeners.class) can be any class you named your listeners.
The #Description is related to allure-reports and as the code snip suggests you can add additional info about the test.
Finally, our Assert.assertEquals(result, true) will take a screen shot in case the assertion fails because we enabled our listener.class to it.

Getting Tab parameters inside LaunchConfigurationDelegate

I have a custom launch configuration. It currently has a JavaArgumentsTab() where I can enter things for VM arguments and Program arguments. But how do I actually get any values entered there?
Ideally I would get them inside my LaunchConfigurationDelegate's launch() method. I expected to find any text entered as arguments inside the LaunchConfiguration or other parameters to that method, and I'm sure this is a newbie question, but I really haven't found anything promising.
TabGroup:
public class LaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
#Override
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
new JavaArgumentsTab(),
new CommonTab()
};
setTabs(tabs);
} }
LaunchConfigurationDelegate:
public class LaunchConfigurationDelegate implements ILaunchConfigurationDelegate {
#Override
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
// How to get anything entered on my Java tab here...?
} }
Everything from the tabs should already have been set as attribute values in the ILaunchConfiguration when launch is called.
The settings from JavaArgumentsTab are stored in the attributes using constants from IJavaLaunchConfigurationConstants.

How does HiveInterruptUtils.interrupt() work?

I've been reading the hive source code recently, but I'm confused by this interrupt(). I want to know how it interrupts the current hive command.The location of this function is in CliDriver.processLine().
In the implementation of HiveInterruptUtils http://people.apache.org/~hashutosh/hive-clover/common/org/apache/hadoop/hive/common/HiveInterruptUtils.html, find this:
public static void interrupt() {
synchronized (interruptCallbacks) {
for (HiveInterruptCallback resource :
new ArrayList<HiveInterruptCallback>(interruptCallbacks)) {
resource.interrupt();
}
}
}
That might interrupts all resources previously added to the HiveInterruptCallback list.
And also the HiveInterruptCallback, http://people.apache.org/~hashutosh/hive-clover/common/org/apache/hadoop/hive/common/HiveInterruptCallback.html#HiveInterruptCallback, is an interface.
public interface HiveInterruptCallback {
/**
* Request interrupting of the processing
*/
void interrupt();
}
The previously registered resources implement HiveInterruptCallback interrupt() method, so the HiveInterruptUtils.interrupt() behavior depends on the specific resource implementation.

arquillian warp timing out instead of executing AfterPhase (or AfterServlet, BeforePhase or BeforeServlet for that matter)

I am just getting started with Arquillian Warp and seems to have hit a stumbling block.
I have a basic UI Test for a registration page
#WarpTest
#RunWith(Arquillian.class)
public class TestProfileEdit extends AbstractUsersTest {
#Drone
FirefoxDriver browser;
#Page
EditProfilePage editProfilePage;
#Page
LoginPage loginPage;
#ArquillianResource
private URL baseURL;
#Deployment
public static Archive<?> createLoginDeployment() throws IOException {
// trimmed for brevity
}
#Before
public void setup() throws MalformedURLException{
final URL loginURL = new URL(baseURL, "login.jsf");
browser.navigate().to(loginURL);
loginPage.login("test#domain.com", "password");
final URL pageURL = new URL(baseURL, "profile/edit.jsf");
System.out.println(pageURL.toExternalForm());
browser.navigate().to(pageURL);
}
#After
public void tearDown() {
browser.manage().deleteAllCookies();
}
#Test
#RunAsClient
public void testSaveData() {
editProfilePage.getDialog().setFirstName("Test First Name");
Warp.execute(new ClientAction() {
#Override
public void action() {
editProfilePage.getDialog().save();
}
}).verify(new TestProfileOnServer());
}
#SuppressWarnings("serial")
public static class TestProfileOnServer extends ServerAssertion {
#Inject
private EntityManager em;
#Inject
private Identity identity;
#Inject
Credentials credentials;
#AfterPhase(Phase.RENDER_RESPONSE)
public void testSavedUserProfile() {
System.out.println("RUNNING TEST");
String username = identity.getUser().getId();
TypedQuery<UserProfile> q = em.createQuery(
"SELECT u from UserProfile u where u.userIdentity.name like :username", UserProfile.class);
UserProfile p;
p = q.setParameter("username", username).getSingleResult();
assertEquals("Test First Name", p.getFirstName());
}
}
}
I have tried the various combinations on testSavedUserProfile() method with absolutely no luck in getting that to trigger.
The test always ends with
java.lang.IllegalStateException: java.util.concurrent.ExecutionException: org.jboss.arquillian.warp.client.execution.AssertionHolder$ServerResponseTimeoutException
I can see the page getting posted and redirected correctly on the firefox window that gets opened up. I tried to get it to not redirect etc. and nothing has helped.
I feel like I am missing something basic and simple but no idea what!
Any help much appreciated.
Thanks.
I've recently encountered a similar problem with Arquillian Warp.
One of the reasons my code didn't get called was that Arquillian merges the server-sde servlet filter into a web archive (WAR) deplyoment only. Neither EAR nor JAR deployments work off the shelf.
For my concrete problem (EAR deployment) I modified the test classes in a way that I merge in the Arquillian filter myself when assembling the tested WAR which in turn is packed into an EAR deployment.
The other problem I ran across was that the AfterServlet event is simply not fired within the unit test execution scope but as part of the servlet filter cleanup code. I believe this logic is totally broken and I build a private fork of the servlet filter which IMHO is handling the logic correctly.

Asynchronous callback - gwt

I am using gwt and postgres for my project. On the front end i have few widgets whose data i am trying to save on to tables at the back-end when i click on "save project" button(this also takes the name for the created project).
In the asynchronous callback part i am setting more than one table. But it is not sending the data properly. I am getting the following error:
org.postgresql.util.PSQLException: ERROR: insert or update on table "entitytype" violates foreign key constraint "entitytype_pname_fkey"
Detail: Key (pname)=(Project Name) is not present in table "project".
But when i do the select statement on project table i can see that the project name is present.
Here is how the callback part looks like:
oksave.addClickHandler(new ClickHandler(){
#Override
public void onClick(ClickEvent event) {
if(erasync == null)
erasync = GWT.create(EntityRelationService.class);
AsyncCallback<Void> callback = new AsyncCallback<Void>(){
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(Void result){ }
};
erasync.setProjects(projectname, callback);
for(int i = 0; i < boundaryPanel.getWidgetCount(); i++){
top = new Integer(boundaryPanel.getWidget(i).getAbsoluteTop()).toString();
left = new Integer(boundaryPanel.getWidget(i).getAbsoluteLeft()).toString();
if(widgetTitle.startsWith("ATTR")){
type = "regular";
erasync.setEntityAttribute(name1, name, type, top, left, projectname, callback);
} else{
erasync.setEntityType(name, top, left, projectname, callback);
}
}
}
Question:
Is it wrong to set more than one in the asynchronous callback where all the other tables are dependent on a particular table?
when i say setProjects in the above code isn't it first completed and then moved on to the next one?
Please any input will be greatly appreciated.
Thank you.
With that foreign key constraint, you must make sure the erasync.setProjects(...) has completed before you insert the rest of the stuff.
I suggest doing the erasync.setEntityAttribute(...) magic in (or from) an onsuccess callback instead of jumping right to it.
You're firing several request in which (guessing from the error message) really should be called in sequence.
Any time you call more than one rpc call; try to think that you should be able to rearrange them in any order (because that's allmost what actually happens because they're asynchronous)... If running them in reverse order does not make sense; you cannot fire them sequentially!
Two ways to fix your problem:
Nesting:
service.callFirst(someData, new AsyncCallback<Void> callback = new AsyncCallback<Void>(){
#Override
public void onFailure(Throwable caught) {/*Handle errors*/}
#Override
public void onSuccess(Void result){
service.callSecond(someOtherData, new AsyncCallback<Void> callback = new AsyncCallback<Void>(){
/* onSuccess and onFailure for second callback here */
});
}
});
Or creating one service call that does both (Recommended):
service.callFirstAndSecond(someData, someOtherData, new AsyncCallback<Void> callback = new AsyncCallback<Void>(){
#Override
public void onFailure(Throwable caught) {/*Handle errors*/}
#Override
public void onSuccess(Void result){
/* Handle success */
}
});
The second option is most likely going to be much less messy, as several nested asynch callbacks quickly grows quite wide and confusing, also you make just one request.
Because of nature of Async, don't assume setProjects(...) method will be called on the server before setEntityAttribute or setEntityType.
Personally, I prefer to have a Project class which contains all necessary info, for example:
public class Project{
private String projectName;
private List attributes = new ArrayList();
.. other properties
// Getter & Setter methods
}
Then send to the server in one round trip:
Project project = new Project();
project.setProjectName(..);
// Set other properties
erasync.saveProjects(project, callback);