Online Books:
java.net on MarkMail:
Search |
||
NetBeans plug-in for AVK - Implementation details of the AVK plug-inPosted by bhavanishankar on June 5, 2006 at 11:54 PM PDT
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. This is what the AVK plug-in does
internally:
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. In the following sections I will be
describing each of the implementation details outlined above. I am
explaining the implementation details in general not being specific to
AVK plug-in.
Step 0. Creating a new NetBeans plug-in project File > New Project > NetBeans
Plug-in Modules > Module Project (Choose Project name[=AVK plug-in]
and code base name[=avkplugin] and leave the rest at default values).
Step
1. Adding an
action to the Projects menuProject > Right Click > New >
Action (Select Category=Tools, Menu=Tools, Position=Update Center -
HERE, Classname=MyAction).
Open layer.xml and change <folder name="Menu"> to <folder name="Projects">
<folder name="Tools"> to <folder name="Tools"> In my case, layer.xml looks like this: <?xml version="1.0" encoding="UTF-8"?>
<!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 package avkplugin;
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: Nodes APIs
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: Let us take an example of reading the
deployment directory of a JavaEE application from the domain.xml
package avkplugin;
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: J2EE Server Registry
Project API Step 2B: Deploying the application by invoking "run-deploy" ANT task This is how one can invoke any build
target from within an action
.....
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 File System API
Ant Ant-Based Project Support Execution API Step 2C: Launching the application in the browser This is how one can launch 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 UI Utilities API
Step 2D: Invoking a tool by creating a new process This is pretty simple. We can just use
the standard JDK Runtime APIs.
String
commandString = System.getProperty("java.home") + File.separator +
"bin" + File.separator + "java" + " -classpath " + "<classpath>"
+ " avkplugin.Tool";
Process p = Runtime.getRuntime().exec(commandString); p.waitFor(); Step 2E: Lauching a window in the editor pane This can be done using the swing UI
builder capabilities of the NetBeans IDE.
Step 3: Source code linking If className, methodName and argument
types are known then we can open the source file and take the
cursor to the exact method. This is how we can achieve that:
.....
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; } public void openSource(String className, String methodName, String[] methodParamTypes) { 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 JMI for Java Language Model
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 : I hope the NetBeans
plug-in developers
find some of the tips and the utility methods described above useful.
Please provide me your comments/feebback.
»
Comments
Comments are listed in date ascending order (oldest first)
|
||
|
|