2015-02-25 15 views
5

Używam agenta Freemarker i DCEVM + HotSwapManager. Zasadniczo pozwala mi to na klasy hotswap nawet przy dodawaniu/usuwaniu metod.Freemarker removeIntrospectionInfo nie działa z DCEVM po modelu hotswap

Wszystko działa jak czar, dopóki Freemarker nie użyje klasy hotswapped jako modelu. Rzucanie freemarker.ext.beans.InvalidPropertyException: Brak takiej właściwości komponentu bean, nawet jeśli odbicie pokazuje, że metoda istnieje (zaznaczona podczas sesji debugowania).

Używam

final Method clearInfoMethod = beanWrapper.getClass().getDeclaredMethod("removeIntrospectionInfo", Class.class); 
clearInfoMethod.setAccessible(true); 
clearInfoMethod.invoke(clazz); 

wyczyścić pamięć podręczną, ale to nie działa. Próbowałem nawet uzyskać pole członkowskie classCache i wyczyścić je za pomocą refleksji, ale to też nie działa.

Co robię źle? Po prostu muszę zmusić freemarkera do wyrzucenia introspekcji na klasę/klasę modelu, którą już zdobył.

Czy jest jakiś sposób?

UPDATE

przykład kod

Application.java

// Application.java 
public class Application 
{ 
    public static final String TEMPLATE_PATH = "TemplatePath"; 
    public static final String DEFAULT_TEMPLATE_PATH = "./"; 

    private static Application INSTANCE; 
    private Configuration freemarkerConfiguration; 
    private BeansWrapper beanWrapper; 

    public static void main(String[] args) 
    { 
     final Application application = new Application(); 
     INSTANCE = application; 
     try 
     { 
      application.run(args); 
     } 
     catch (InterruptedException e) 
     { 
      System.out.println("Exiting"); 
     } 
     catch (IOException e) 
     { 
      System.out.println("IO Error"); 
      e.printStackTrace(); 
     } 
    } 

    public Configuration getFreemarkerConfiguration() 
    { 
     return freemarkerConfiguration; 
    } 

    public static Application getInstance() 
    { 
     return INSTANCE; 
    } 

    private void run(String[] args) throws InterruptedException, IOException 
    { 
     final String templatePath = System.getProperty(TEMPLATE_PATH) != null 
       ? System.getProperty(TEMPLATE_PATH) 
       : DEFAULT_TEMPLATE_PATH; 

     final Configuration configuration = new Configuration(); 
     freemarkerConfiguration = configuration; 

     beanWrapper = new BeansWrapper(); 
     beanWrapper.setUseCache(false); 
     configuration.setObjectWrapper(beanWrapper); 
     try 
     { 
      final File templateDir = new File(templatePath); 
      configuration.setTemplateLoader(new FileTemplateLoader(templateDir)); 
     } 
     catch (IOException e) 
     { 
      throw new RuntimeException(e); 
     } 

     final RunnerImpl runner = new RunnerImpl(); 
     try 
     { 
      runner.run(args); 
     } 
     catch (RuntimeException e) 
     { 
      e.printStackTrace(); 
     } 
    } 

    public BeansWrapper getBeanWrapper() 
    { 
     return beanWrapper; 
    } 
} 

RunnerImpl.java

// RunnerImpl.java 
public class RunnerImpl implements Runner 
{ 
    @Override 
    public void run(String[] args) throws InterruptedException 
    { 
     long counter = 0; 
     while(true) 
     { 
      ++counter; 
      System.out.printf("Run %d\n", counter); 
//   Application.getInstance().getFreemarkerConfiguration().setObjectWrapper(new BeansWrapper()); 
      Application.getInstance().getBeanWrapper().clearClassIntrospecitonCache(); 
      final Worker worker = new Worker(); 
      worker.doWork(); 
      Thread.sleep(1000); 
     } 
    } 

Worker.java

// Worker.java 
public class Worker 
{ 
    void doWork() 
    { 
     final Application application = Application.getInstance(); 
     final Configuration freemarkerConfiguration = application.getFreemarkerConfiguration(); 

     try 
     { 
      final Template template = freemarkerConfiguration.getTemplate("test.ftl"); 
      final Model model = new Model(); 
      final PrintWriter printWriter = new PrintWriter(System.out); 

      printObjectInto(model); 
      System.out.println("-----TEMPLATE MACRO PROCESSING-----"); 
      template.process(model, printWriter); 
      System.out.println(); 
      System.out.println("-----END OF PROCESSING------"); 
      System.out.println(); 
     } 
     catch (IOException e) 
     { 
      e.printStackTrace(); 
     } 
     catch (TemplateException e) 
     { 
      e.printStackTrace(); 
     } 
    } 

    private void printObjectInto(Object o) 
    { 
     final Class<?> aClass = o.getClass(); 
     final Method[] methods = aClass.getDeclaredMethods(); 
     for (final Method method : methods) 
     { 
      System.out.println(String.format("Method name: %s, public: %s", method.getName(), Modifier.isPublic(method.getModifiers()))); 
     } 
    } 
} 

Model.java

// Model.java  
public class Model 
{ 
    public String getMessage() 
    { 
     return "Hello"; 
    } 

    public String getAnotherMessage() 
    { 
     return "Hello World!"; 
    } 
} 

Przykład ten nie działa w ogóle. Nawet zmiana BeansWrapper podczas działania nie będzie miała żadnego efektu.

+0

Zakładając, że wywołasz metodę na 'BeansWrapper', która jest aktualnie używana, powinna działać. Ale 'removeFromClassIntrospectionCache (Class clazz)' jest metodą publiczną, dlaczego tak ją nazywasz? Jeśli jest to stary FreeMarker, spróbuj najpierw aktualizacji. – ddekany

+0

@ddany: Mam do czynienia z tym samym problemem co Martin. Używam wersji 2.3.19, więc uaktualnienie powinno wystarczyć. Jednak komentarz Javadoc do 'removeFromClassIntrospectionCache' zadaje mi pytania:" '... Jeśli klasa będzie nadal używana, wpis w pamięci podręcznej zostanie cicho odtworzony" "- oznacza to, że gdy pamięć podręczna szablonu zawiera mapowanie dla klasy pytanie, czy introspekcja nie dostanie jej przeładowanej wersji? Dzięki! – plesatejvlk

+0

Czyściłem jedyną BeansWrapper, którą ustawiłem na obiekt konfiguracji freemarkera. Zobacz aktualizację na przykładzie. –

Odpowiedz

3

BeansWrapper (i DefaultObjectWrapper itd.) Pamięć podręczna introspekcji opiera się na java.beans.Introspector.getBeanInfo(aClass), a nie na odbiciu. (Dzieje się tak, ponieważ traktuje obiekty jako JavaBeans.) java.beans.Introspector ma swoją wewnętrzną pamięć podręczną, więc może zwracać nieaktualne informacje iw takim przypadku BeansWrapper po prostu odtworzy własne dane introspekcji klasy na podstawie tych nieaktualnych informacji. Od momentu buforowania java.beans.Introspector jest to prawda, ponieważ opiera się na założeniu, że klasy w Javie są niezmienne. Jeśli coś złamie tę podstawową zasadę, powinno się upewnić, że pamięć podręczna java.beans.Introspector jest wyczyszczona (i wiele innych pamięci podręcznych ...), inaczej nie będzie to tylko FreeMarker, który się zepsuje. Na przykład w JRebel robili wiele wysiłku, aby wyczyścić wszystkie rodzaje pamięci podręcznych. Sądzę, że DCEVM nie ma na to środków. Wydaje się więc, że sam musisz zadzwonić pod numer Introspector.flushCaches().

Aktualizacja: Przez pewien czas (Java 7, może 6) java.beans.Introspector ma jeden bufor na wątek grupy, więc trzeba zadzwonić flushCaches() z każdego wątku. A to wszystko jest w rzeczywistości szczegółem wdrożenia, który w zasadzie może się zmienić w każdej chwili. I niestety, JavaDoc z Introspector.flushCaches() nie ostrzega ...

+0

To interesujące, dzięki! – plesatejvlk

+0

Właśnie odkryłem naprawdę dziwną rzecz. Zastosowałem to rozwiązanie do naszej aplikacji działającej na WLS i przestało działać. Odkryłem, że introspektor zwraca inne wartości, które są wywoływane przez HotSwapAgent i wątek z WLS WorkerThreadPool. Każdy pomysł, dlaczego tak się dzieje i jak to naprawić? –

+0

Podejrzewam, co się dzieje ... zobacz aktualizację. – ddekany