NetBeans plug-in for AVK - Implementation details of the AVK plug-in
My previous blog entry explained about AVK and how to use AVK from within NetBeans.
I thought it is worthwhile spending some time in writing down the detailed description of the tasks involved in writing this plug-in. This article is intended for the NetBeans plug-in developers.
Implementation details of AVK plug-in :
1. Adds "Dynamic Verification" action to "Verify Project" task.
2. When "Dynamic Verification" action is invoked then the action handler
A. Configures application server to run in AVK mode (by invoking MBeans deployed on the application server through JMX APIs).
B. Deploys the application (by invoking "run-deploy" ANT task)
C. Lauches the application in the browser if the application has web component.
D. Invokes the AVK tool (by creating a seperate process)
E. Launches "AVK Session" window showing the dynamic verification results.
3. Source code linking from the swing UI : From the "AVK Session" window - Right click "Servlet Name" or "Method Name" > "Go To Source" to view the appropriate source code of your application.
Step 0. Creating a new NetBeans plug-in project
Step 1. Adding an action to the Projects menu
Open layer.xml and change
<folder name="Tools"> to <folder name="Tools">
In my case, layer.xml looks like this:
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Actions">
<folder name="Tools">
<file name="avkplugin-MyAction.instance"/>
</folder>
</folder>
<folder name="Projects">
<folder name="Actions">
<attr name="org-netbeans-modules-autoupdate-UpdateAction.instance/avkplugin-MyAction.shadow" boolvalue="true"/>
<file name="avkplugin-MyAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/avkplugin-MyAction.instance"/>
</file>
</folder>
</folder>
</filesystem>
Change MyAction.java to inherit from NodeAction instead of from CallableSystemAction. Implement performAction(...) method. In my case I simply print 'My action is invoked' message. Here is my MyAction.java
import org.openide.nodes.Node;
import org.openide.nodes.NodeAcceptor;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.NodeAction;
public final class MyAction extends NodeAction {
public void performAction(Node[] nodes) {
System.out.println("My action is invoked...");
}
public String getName() {
return NbBundle.getMessage(MyAction.class, "CTL_MyAction");
}
protected void initialize() {
super.initialize();
// see org.openide.util.actions.SystemAction.iconResource() javadoc for more details
putValue("noIconInMenu", Boolean.TRUE);
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
protected boolean asynchronous() {
return false;
}
protected boolean enable(Node[] node) {
return true;
}
}
At this point, my project uses the following libraries:
Utilities APIs
To install the plug-in : Project > Right Click > Install/Reload in Development IDE.
After the plug-in is successfully installed "My Action" action gets added to the Project. To invoke the action : Project > Right Click > My Action. You will see "My action is invoked" on the console from where you launched the IDE.
Step 2A: Invoking MBeans deployed on the application server using JMX APIs:
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.netbeans.api.project.Project;
import org.netbeans.modules.j2ee.deployment.devmodules.spi.J2eeModuleProvider;
import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.actions.NodeAction;
public final class MyAction extends NodeAction {
public void performAction(Node[] nodes) {
System.out.println("Deployment directory = " + getDeployDir(getProject(nodes[0])));
}
public Project getProject(Node projectNode) {
Lookup lookup = projectNode.getLookup();
Project project = (Project)lookup.lookup(Project.class);
return project;
}
public String getDeployDir(Project project) {
Lookup projectLookup = project.getLookup();
J2eeModuleProvider moduleProvider = (J2eeModuleProvider)projectLookup.lookup(J2eeModuleProvider.class);
String moduleName = moduleProvider.getDeploymentName().toLowerCase();
String objectName = "com.sun.appserv:type=j2ee-application,name=" + moduleName + ",category=config";
InstanceProperties instProps = moduleProvider.getInstanceProperties();
String deployDir = null;
String adminUser = instProps.getProperty("username");
String adminPassword = instProps.getProperty("password");
String adminPort = instProps.getProperty("httpportnumber");
String jmxUrl = "service:jmx:rmi:///jndi/rmi://localhost:8686/jmxrmi"; // hardcoded.
String[] credentials = new String[] { adminUser, adminPassword };
Map env = new HashMap();
env.put("jmx.remote.credentials", credentials);
try {
JMXServiceURL url = new JMXServiceURL(jmxUrl);
JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
deployDir = (String)mbsc.getAttribute(new ObjectName(objectName), "location");
} catch(Exception ex) {
ex.printStackTrace();
}
return deployDir;
}
.....
}
At this point, my project uses the following additional libraries:
Project API
Step 2B: Deploying the application by invoking "run-deploy" ANT task
import org.apache.tools.ant.module.api.support.ActionUtils;
import org.netbeans.spi.project.support.ant.GeneratedFilesHelper;
import org.openide.filesystems.FileObject;
.....
public final class MyAction extends NodeAction {
public void performAction(Node[] nodes) {
runBuildTarget(getProject(nodes[0]), "run-deploy");
}
.....
public void runBuildTarget(Project project, String targetName) {
FileObject buildXML = project.getProjectDirectory().getFileObject(GeneratedFilesHelper.BUILD_XML_PATH);
try {
ActionUtils.runTarget(buildXML, new String[]{targetName}, new Properties());
} catch (Exception ex) {
ex.printStackTrace();
}
}
.....
}
Additional libraries needed for this purpose
Ant
Ant-Based Project Support
Execution API
Step 2C: Launching the application in the browser
import org.openide.awt.HtmlBrowser.URLDisplayer;
.....
public final class MyAction extends NodeAction {
.....
public void launchApp(Project project) {
Lookup projectLookup = project.getLookup();
J2eeModuleProvider moduleProvider = (J2eeModuleProvider)projectLookup.lookup(J2eeModuleProvider.class);
String contextRoot = moduleProvider.getConfigSupport().getWebContextRoot();
String httpPort = "8080"; // hardcoded. Correct way of getting it is : mbsc.getAttribute(new javax.management.ObjectName("com.sun.appserv:type=http-listener,id=http-listener-1,config=server-config,category=config") ,"port");
String url = "http://localhost:" + httpPort + "/" + contextRoot;
try {
URLDisplayer.getDefault().showURL(new URL(url));
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
}
.....
}
Additional libraries needed for this purpose
Step 2D: Invoking a tool by creating a new process
Process p = Runtime.getRuntime().exec(commandString);
p.waitFor();
Step 2E: Lauching a window in the editor pane
Step 3: Source code linking
import org.netbeans.jmi.javamodel.Element;
import org.netbeans.jmi.javamodel.JavaClass;
import org.netbeans.jmi.javamodel.Method;
import org.netbeans.jmi.javamodel.Resource;
import org.netbeans.jmi.javamodel.Type;
import org.netbeans.modules.java.JavaEditor;
import org.netbeans.modules.javacore.api.JavaModel;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.openide.loaders.DataObject;
import org.openide.text.PositionBounds;
.....
public final class MyAction extends NodeAction {
.....
private List getMethodParams(String[] paramTypes) {
List methodParams = new ArrayList();
for(int i=0; i<paramTypes.length; i++) {
String param = paramTypes[i];
Type type = JavaModel.getDefaultExtent().getType().resolve(param);
methodParams.add(type);
}
return methodParams;
}
List methodParams = getMethodParams(methodParamTypes);
JavaClass javaClass = (JavaClass)JavaModel.getDefaultExtent().getType().resolve(className);
FileObject fo = JavaModel.getFileObject(javaClass.getResource());
try {
if(methodName != null) {
Resource resource = JavaModel.getResource(fo);
JavaMetamodel javaMetamodel = JavaMetamodel.getManager();
Method m = javaClass.getMethod(methodName,methodParams,true);
Element element = resource.getElementByOffset(m.getStartOffset());
PositionBounds position = javaMetamodel.getElementPosition(element);
((JavaEditor) javaMetamodel.getDataObject(element.getResource()).getCookie(JavaEditor.class)).openAt(position.getBegin());
} else {
DataObject dobj = DataObject.find(fo);
OpenCookie oc = (OpenCookie)dobj.getCookie(OpenCookie.class);
oc.open();
}
} catch (Throwable t) {
StatusDisplayer.getDefault().setStatusText("Unable to open " + className);
t.printStackTrace();
}
}
.....
}
Additional libraries needed for this purpose
Java Language Model Implementation
JMI Reflective API
Text API
Java Source Files
Datasystems API
Window System API
JMI Utilities
Note : Add <compile-dependency/> for <code-name-base>org.netbeans.modules.jmiutils</code-name-base>
Conclusion :





