Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Динамическая перезагрузка Java EE приложений

Динамическая перезагрузка Java EE приложений

Динамическая перезагрузка Java EE приложений

Anton Arhipov

April 24, 2013
Tweet

More Decks by Anton Arhipov

Other Decks in Technology

Transcript

  1. Мы  хотим  знать   • Куда  уходит  время?   • Как  спасти

     время?   • Что  делает  JRebel?   • Как  O_o!?   • И  какое  отношение  это  всё   имеет  к  Java  EE?  
  2. Цикл  разработки   Написали  код  /   изменили   конфигурацию

      Компиляция,   Сборка,   Перезапуск   контейнера   Результат   В  СРЕДНЕМ:   2.5-­‐3  МИНУТЫ  
  3. Сборка   Запаковать  всё  в  WAR/EAR   Запаковать  артефакты  в

     JAR-­‐ы   Скомпилировать  код   Скопировать  ресурсы   Распознать  зависимости  
  4. Отмазки   Каждый  раз,  когда  я  слышу,      “Because

     our  project  structure            is  so  complex  …”  
  5. или…   “…I  know  about  JRebel,  but  I   cannot

     pay  for  it,  so  I  tried   Maven…”    
  6. Сборка   Запаковать  всё  в  WAR/EAR   Запаковать  артефакты  в

     JAR-­‐ы   Скомпилировать  код   Скопировать  ресурсы   Распознать  зависимости  
  7. Сборка   Запаковать  всё  в  WAR/EAR   Запаковать  артефакты  в

     JAR-­‐ы   Скомпилировать  код   Скопировать  ресурсы   Распознать  зависимости  
  8. ~1  мин   30  сек  –  30  мин   1-­‐60

     сек   Навигация  по  приложению   Запуск  приложения   Запуск  контейнера  
  9. Запуск  приложения   •  Сканирование  CLASSPATH   •  Сканирование  файловой

     системы   •  Зачитка  конфигураций   •  Инструментация  /  генерация  кода   •  @Inject  
  10. Внешнее   (БД,  файлы,  итд)   Временное   (кеш)  

    Сериализуемое   (сессия)   Производное   (конфигурации)   Состояние  
  11. Как  перезагрузить  класс?   Старый   загрузчик   Мой  класс

      Объект  в   памяти   Новый   загрузчик   Мой  новый   класс   Объект  в   памяти   Перенести  состояние  
  12. Classes Libraries OldClassLoader NewClassLoader Sevlet New Classes New Libraries Sevlet

    Session Session init()   App State App State Serialize/deserialize  
  13. Вывод   Загрузчики  классов  не  подходят   не  эффективны  для

     перезагрузки   классов  или  приложений!  
  14. MyObject MyObject.class   OldClassLoader Code   101000101   100010010  

    Debugger HotSwap   New  code   111000100   101010010   New  code   111000100   101010010   HotSwap   Make  changes  
  15. JRebel  vs  HotSwap   HotSwap   JRebel   Changing  method

     bodies   +   +   Adding/removing  methods   -­‐   +   Adding/removing  constructors   -­‐   +   Adding/removing  fields   -­‐   +   Adding/removing  classes   -­‐   +   Adding/removing  annotasons   -­‐   +   Replacing  superclass   -­‐   -­‐   Adding/removing  implemented   interfaces   -­‐   -­‐  
  16. • Есть  надежда:   – JEP  159:  Enhanced  Class  Redefinison    

    • Но  одной  только  возможности   перегружать  классы   недостаточно!  
  17. MyObject MyObject.class   OldClassLoader Code   101000101   100010010  

    New  code   111000100   101010010   JRebel   Framework   Configurason   (XML,  annotasons,..)   JRebel   Make  changes  
  18. Что  бы  сделать  JRebel,   нам  потребовалось  …    

    Внедриться  в  процесс     загрузки  классов  
  19. Потребовалось  …     проинтегрировать  решение     со  всевозможными*

     серверами   приложений  (JBoss,  Glassfish,  итд)   *  -­‐  На  данный  момент  в  тестовой  среде   установлено  60  разных  версий  серверов  
  20. Здесь  –  бесстыдная  реклама   завтрашнего  доклада:     9.30,

       в  зале  Moscow,   Загрузчики  классов  в  Java:   коллекция  граблей  
  21. JRebel   core   Spring   plugin   Hibernate  

    plugin   Log4j   plugin   Jersey   plugin   Weld   plugin   ADF   plugin   Mojarra   plugin   Guice   plugin   Jasper   plugin  
  22. • JRebel  core  –  отвечает  за   перегрузку  классов  (Java  кода)

      • Расширения  (i.e.  Spring  plugin)  –   отвечают  за  перегрузку   конфигураций  и  метаданных  
  23. @Path(“/”)   public  String  foo()  {      return  “FooBar”;

      }   @Path(“/foobar”)   public  String  foo()  {      return  “FooBar”;   }   изменение   JRebel   core   перегузка   класса   Spring   plugin  
  24. @Path(“/”)   public  String  foo()  {      return  “FooBar”;

      }   @Path(“/foobar”)   public  String  foo()  {      return  “FooBar”;   }   изменение   JRebel   core   перегузка   класса   Spring   plugin  
  25. Java   App   Framework   Conf   XML  

    HTTP request 1) Stop request
  26. Java   App   Framework   Conf   XML  

    HTTP request 2) Check resources
  27. Java   App   Framework   Conf   XML  

    HTTP request 3) Reconfigure
  28. Java   App   Framework   Conf   XML  

    HTTP request 4) Resume request
  29. «Агент» •  Добавить –javaagent для внедрения в процесс загрузки классов

    •  Реализовать свой ClassFileTransformer •  Использовать инструменты для манипуляции с Java байткодом (Javassist, cglib, asm) для реализации своей логики java.lang.instrument  
  30. java.lang.instrument import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; public class Agent { public

    static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { … }); } } META-INF/MANIFEST.MF Premain-Class: Agent java  –javaagent:agent.jar  …  
  31. java.lang.instrument import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; public class Agent { public

    static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { … }); } } META-INF/MANIFEST.MF Premain-Class: Agent java  –javaagent:agent.jar  …  
  32. ClassFileTransformer new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className,

    Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); } }
  33. ClassFileTransformer new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className,

    Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); } }
  34. ClassFileTransformer new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className,

    Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); } }
  35. ClassFileTransformer new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className,

    Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); } } Enter JRebel SDK
  36. JRebel SDK class MyPlugin implements Plugin { public void preinit()

    { IntegrationFactory.getInstance(). addIntegrationProcessor( getClass().getClassLoader(), "org.jboss.Registry", new RegistryCBP()); … } } Нужна  привязка     к  загрузчику   There  be  dragons!  
  37. JRebel SDK class MyPlugin implements Plugin { public void preinit()

    { ReloaderFactory.getInstance().                    addClassReloadListener(                          new  ClassEventListener()  {                                public  void  onClassEvent(int  type,  Class  c)  {   } }); } }
  38. JRebel SDK public class RegistryCBP extends JavassistClassBytecodeProcessor { public void

    process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception { cp.importPackage("org.zeroturnaround.javarebel");
  39. JRebel SDK public class RegistryCBP extends JavassistClassBytecodeProcessor { public void

    process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception { cp.importPackage("org.zeroturnaround.javarebel");
  40. JRebel SDK public class RegistryCBP extends JavassistClassBytecodeProcessor { public void

    process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception { for  (CtConstructor  c  :  ctClass.getConstructors())  {                          if  (c.callsSuper())                                c.insertA‰er("ReloaderFactory.getInstance().  ”  +                                                                              "addClassReloadListener($0);");              
  41. JRebel SDK public class RegistryCBP extends JavassistClassBytecodeProcessor { public void

    process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception { ctClass.addInterface(cp.get(                                    ClassEventListener.class.getName()));                ctClass.addMethod(CtNewMethod.make(                                  "public  void  onClassEvent(int  type,  Class  clazz)  {"
  42. Javassist •  Bytecode manipulation made easy •  Source-level and bytecode-level

    API •  Uses the vocabulary of Java language •  On-the-fly compilation of the injected code •  http://www.jboss.org/javassist
  43. Insert Before CtClass clazz = ... CtMethod method = clazz.getMethod(“dispatch”,

    “(V)V”); m.insertBefore( “{ Reloader reloader = “ + “ReloaderFactory.getInstance();” + “reloader.checkAndReload(SomeClass.class); ” + “Config.init(); }“ );
  44. Добавление интерфейса ClassPool  cp  =  ClassPool.getDefault();     CtClass  ct

     =  cp.get("org.zt.Alarm");     ct.addInterface(cp.get(Listener.class.getName()));     ct.addMethod(CtNewMethod.make("public  void  fire(){  alert();  }",  ct));   public  interface  Listener  {        void  fire();     }   public  class  Alarm  {        void  alert()  {}     }  
  45. Добавление интерфейса ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.get("org.zt.Alarm");

    ct.addInterface(cp.get(Listener.class.getName())); ct.addMethod(CtNewMethod.make("public void fire() { alert(); }", ct)); public  interface  Listener  {        void  fire();     }   public  class  Alarm  {        void  alert()  {}     }  
  46. Добавление интерфейса ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.get("org.zt.Alarm");

    ct.addInterface(cp.get(Listener.class.getName())); ct.addMethod(CtNewMethod.make("public void fire() { alert(); }", ct)); public  interface  Listener  {        void  fire();     }   public  class  Alarm  {        void  alert()  {}     }  
  47. Перехват вызовов        ClassPool  pool  =  ClassPool.getDefault();  

             CtClass  ct  =  pool.get("org.zt.Config");            ct.getDeclaredMethod("process")                .instrument(new  ExprEditor()  {                        public  void  edit(NewExpr  e)                                    throws  CannotCompileExcepson  {                                  e.replace("$_  =  $proceed($$);");                        }          });  
  48. Перехват вызовов        ClassPool  pool  =  ClassPool.getDefault();  

             CtClass  ct  =  pool.get("org.zt.Config");            ct.getDeclaredMethod("process")                .instrument(new  ExprEditor()  {                        public  void  edit(NewExpr  e)                                    throws  CannotCompileExcepson  {                                  e.replace("$_  =  $proceed($$);");                        }          });  
  49. Копирование методов CtClass clazz = ... CtMethod m = clazz.getMethod("configure",

    "(V)V"); CtMethod copy = CtNewMethod.copy( m, "__configure", clazz, null); … clazz.addMethod(CtNewMethod.make( "public void onClassEvent(int type, Class clazz) {" + "__configure();" + "} ");
  50. Копирование методов CtClass clazz = ... CtMethod m = clazz.getMethod("configure",

    "(V)V"); CtMethod copy = CtNewMethod.copy( m, "__configure", clazz, null); … clazz.addMethod(CtNewMethod.make( "public void onClassEvent(int type, Class clazz) {" + "__configure();" + "} ");
  51. Копирование методов CtClass clazz = ... CtMethod m = clazz.getMethod("configure",

    "(V)V"); CtMethod copy = CtNewMethod.copy( m, "__configure", clazz, null); … clazz.addMethod(CtNewMethod.make( "public void onClassEvent(int type, Class clazz) {" + "__configure();" + "} ");