NetBeans Plugin Development: JRebel Experience Report

1,136 views
1,023 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,136
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
12
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide
  • List the requirements for the plugin
  • NetBeans Plugin Development: JRebel Experience Report

    1. 1. NetBeans Plugin Development: JRebel Experience Report JavaOne SF 2012
    2. 2. whoamiAnton ArhipovJRebel Product Lead@antonarhipovanton@zeroturnaround.com
    3. 3. Agenda• JRebel intro – Plugin requirements• NetBeans plugin development• Integrating with NetBeans server adaptors• Integrating with NetBeans debugger• Packaging & publishing
    4. 4. ENTER JREBEL
    5. 5. The Turnaround Cycle Make changes AVG 2.5 min Build,Observe deploy, results wait
    6. 6. Make changes in IDE ClassLoaderFramework MyObject.class Code 101000101 New code JRebel 100010010 111000100 101010010 MyObject Configuration (XML, annotations,..)
    7. 7. in action
    8. 8. Why IDE plugin? UsabilityAutomation Debugger
    9. 9. JRebel Plugin for NetBeans Workbench Settings, UX JRebel PluginNetBeans Process VM args Launcher Breakpoints Debugger Stepping
    10. 10. ENTER NETBEANSPLUGINS
    11. 11. Books
    12. 12. UI Elements• Buttons/Actions• Toolbars/Menus• Settings/Options• Popups• etc
    13. 13. layers.xml<folder name="Actions"> <folder name="JRebel"> <file name="JRebelToggleAction.instance"> <attr name="delegate" newvalue="o.z.j.n.JRebelToggleAction"/> </file> </folder></folder><folder name="Toolbars"> <folder name="Build"> <file name="JRebelToggleAction.shadow"> <attr name="originalFile" stringvalue="Actions/JRebel/JRebelToggleAction.instance"/> <attr name="position" intvalue="250"/>
    14. 14. Options
    15. 15. Options (Old API)<folder name="OptionsDialog"> <file name="JRebelOptions.instance"> <attr name="instanceCreate" methodvalue="o.n.s.o.OptionsCategory.createCategory"/> <attr name="controller" newvalue="o.z.j.n.JRebelOptionsPanelController"/> </file></folder><folder name="Services"> <file name="org-zeroturnaround-jrebel-netbeans-options.settings" url="options.xml"/></folder>
    16. 16. Options (New API)@OptionsPanelController.SubRegistration( location = "Advanced", displayName = "#AdvancedOption_DisplayName_Super", keywords = "#AdvancedOption_Keywords_Super", keywordsCategory = "Advanced/Super")@org.openide.util.NbBundle.Messages( {"AdvancedOption_DisplayName_Super=Super", "AdvancedOption_Keywords_Super=super"})public final class SuperOptionsPanelController extends OptionsPanelController {
    17. 17. CUSTOM JVMARGUMENTS
    18. 18. JVM arguments• JRebel is bootstrapped using JVM arguments: -javaagent:/path/to/jrebel.jar -noverify -Xbootclasspath/p:jrebel-bootstrap.jar;jrebel.jar -Drebel.log=true -Drebel.jersey_plugin=true• Various NetBeans project types pass JVM arguments slightly differently: Web project VS Maven project JBoss VS Tomcat VS Glassfish
    19. 19. Server settings: Tomcat Can put your arguments here
    20. 20. Server settings: Glassfish No VM arguments?
    21. 21. Project DeploymentAha!!
    22. 22. ToggleSimplest, from the user’s point of view: should not care about project type of runtime differences
    23. 23. Togglepublic final class JRebelToggleAction extends BooleanStateAction { public void initialize() { super.initialize(); setBooleanState(JRebelSettings.isEnabled()); } public void actionPerformed(ActionEvent ev) { super.actionPerformed(ev); JRebelSettings.setEnabled(getBooleanState()); // NbPreferences.forModule(JRebelSettings.class) // .putBoolean(“enabled”, true); }}
    24. 24. Challenge No extension points provided by the NetBeans platform Load-Time Weaving of NetBeans platform classesMight not be the brightest bulb idea, but seems to work fine 
    25. 25. PATCHING THEPLATFORM
    26. 26. org.openide.modules.ModuleInstallpublic class Installer extends ModuleInstall { @Override public void restored() { // patch platform classes here }}
    27. 27. PatcherString classToPatch = …ClassLoader cl = Lookup.getDefault() .lookup(ClassLoader.class);Object o = ClassLoader#findLoadedClass(classToPatch )ClassPool cp = new ClassPool();cp.appendClassPath(new LoaderClassPath(cl));CtClass ctc = cp.get(classToPatch);patch(cp, ctc);ctc.toClass(cl, null);
    28. 28. PatcherString classToPatch = …ClassLoader cl = Lookup.getDefault() .lookup(ClassLoader.class); e.g.Object o = ClassLoader#findLoadedClass(classToPatch ) “org.netbeans.modules.glassfish.common.StartTask”“org.netbeans.modules.tomcat5.ide.StartTomcat$StartRunnable”ClassPool cp = new ClassPool(); etccp.appendClassPath(new LoaderClassPath(cl));CtClass ctc = cp.get(classToPatch);patch(cp, ctc);ctc.toClass(cl, null);
    29. 29. PatcherString classToPatch = …ClassLoader cl = Lookup.getDefault() .lookup(ClassLoader.class);Object o = ClassLoader#findLoadedClass(classToPatch ) a class loader capable for findingClassPool cp = new ClassPool(); module resources from anycp.appendClassPath(new LoaderClassPath(cl));CtClass ctc = cp.get(classToPatch);patch(cp, ctc);ctc.toClass(cl, null);
    30. 30. PatcherString classToPatch = …ClassLoader cl = Lookup.getDefault() .lookup(ClassLoader.class);Object o = ClassLoader#findLoadedClass(classToPatch )ClassPool= ClassLoader.classMethod m cp = new ClassPool();cp.appendClassPath(new LoaderClassPath(cl)); .getDeclaredMethod("findLoadedClass", String.class);CtClass ctc = cp.get(classToPatch);m.setAccessible(true);patch(cp, ctc);Object o = m.invoke(cl, patchedClassName);ctc.toClass(cl, null);
    31. 31. PatcherString classToPatch = …ClassLoader cl = Lookup.getDefault() Enter Javassist .lookup(ClassLoader.class);Object o = ClassLoader#findLoadedClass(classToPatch )ClassPool cp = new ClassPool();cp.appendClassPath(new LoaderClassPath(cl));CtClass ctc = cp.get(classToPatch);patch(cp, ctc);ctc.toClass(cl, null);
    32. 32. PatcherString classToPatch = …ClassLoader cl = Lookup.getDefault() .lookup(ClassLoader.class);Object o = ClassLoader#findLoadedClass(classToPatch )ClassPool cp = new ClassPool();cp.appendClassPath(new LoaderClassPath(cl));CtClass ctc = cp.get(classToPatch); This is where patchingpatch(cp, ctc); happens reallyctc.toClass(cl, null);
    33. 33. patch(…)final CtMethod method = ctc.getDeclaredMethod("run");method.instrument(new ExprEditor() { public void edit(MethodCall m) { if ("getJavaOpts".equals(m.getMethodName())) { m.replace( "if (command == CommandType.START)" + OPTS + "$_ = opts + $proceed($$);"); } }});
    34. 34. patch(…)final CtMethod method = ctc.getDeclaredMethod("run");method.instrument(new ExprEditor() { public void edit(MethodCall m) { Patching if ("getJavaOpts".equals(m.getMethodName())) { StartTomcat$StartRunnable#run() m.replace( for Tomcat launcher "if (command == CommandType.START)" + OPTS + "$_ = opts + $proceed($$);"); } }});
    35. 35. patch(…)final CtMethod method = ctc.getDeclaredMethod("run");method.instrument(new ExprEditor() { public void edit(MethodCall m) { if ("getJavaOpts".equals(m.getMethodName())) { m.replace( "if (command == CommandType.START)" + OPTS + "$_ =Should+append custom options opts $proceed($$);"); } to getJavaOpts result }});
    36. 36. patch(…) final CtMethod method = ctc.getDeclaredMethod("run");String OPTS ="ClassLoader cl = (ClassLoader) Lookup.getDefault().lookup(ClassLoader.class);" + method.instrument(new ExprEditor() {"Class locator = cl.loadClass("org.zeroturnaround.jrebel.netbeans.JRebelLocator");" + public void edit(MethodCall m) {"Method getJRebelOptsStr = locator.getMethod("getJRebelOptsStr", null);" + if ("getJavaOpts".equals(m.getMethodName())) {"String opts = (String) getJRebelOptsStr.invoke(null, null);"; m.replace( "if (command == CommandType.START)" + OPTS + "$_ = opts + $proceed($$);"); } } });
    37. 37. patch(…) final CtMethod method = ctc.getDeclaredMethod("run");String OPTS ="ClassLoader cl = (ClassLoader) Lookup.getDefault().lookup(ClassLoader.class);" + method.instrument(new ExprEditor() {"Class locator = cl.loadClass("org.zeroturnaround.jrebel.netbeans.JRebelLocator");" + public void edit(MethodCall m) {"Method getJRebelOptsStr = locator.getMethod("getJRebelOptsStr", null);" + if ("getJavaOpts".equals(m.getMethodName())) {"String opts = (String) getJRebelOptsStr.invoke(null, null);"; m.replace( "if (command == CommandType.START)" + OPTS + "$_ = opts + $proceed($$);"); } -javaagent:/path/to/jrebel.jar etc } });
    38. 38. Pros & Cons+ Can get stuff done even if the platform doesn’tprovide a proper interface- Brittle for maintenance
    39. 39. New API (7.2)• SPI for JVM options passed to SE program or EE server (Bug 206196)@StartupArgumentsProvider.Registration( position=10, displayName="#DESC_JRebel", startMode={StartMode.NORMAL, StartMode.DEBUG})public class MyArgsProvider implements StartupArgumentsProvider {@Overridepublic List<String> getArguments(ServerInstance instance, StartMode mode)
    40. 40. Dude, where’s my car breakpoint?DEBUGGERINTEGRATION
    41. 41. BreakpointsLine 11: initial breakpoint
    42. 42. BreakpointsLine 14: same breakpoint, but new version of the classNeed to “transfer” the breakpointfrom original class to versioned class
    43. 43. Patching• o.n.m.d.jpda.breakpoints.LineBreakpointImpl – setRequests – getLocations
    44. 44. New API (7.3) • Allow to provide additional binary classes to submit breakpoints on (Bug 215680)@BreakpointClassFilter.Registrationpublic class MyFilter extends BreakpointClassFilter { @Override public ClassNames filterClassNames(ClassNames classNames, JPDABreakpoint breakpoint){}}
    45. 45. RELEASING THE PLUGIN
    46. 46. Packaging
    47. 47. Packaging Plugin has to be signed Module descriptor JRebel distribution Dependencies Plugin itself
    48. 48. Maven Build• NetBeans modules maven plugin – nbm-maven-plugin• Properties maven plugin – properties-maven-plugin• Maven dependency plugin – maven-dependency-plugin• Maven compiler plugin• Maven JAR plugin
    49. 49. Publishing• http://plugins.netbeans.org/• Verification is performed manually (by volunteers)• Main criteria: should not break the platform• Takes some time
    50. 50. Lessons Learned• UX is hard• Follow the logic of the target IDE• Follow the new APIs• Annotations over layer.xml• If no required extension point provided, can get around with patching via ModuleInstall• Publishing is not 100% predictable process
    51. 51. Credits• Big thanks to NetBeans team for awesome co- operation & providing the new API for debugger and server adaptors!

    ×