2015-02-25 32 views
5

Uso l'agente Freemarker e DCEVM + HotSwapManager. Questo in pratica mi permette di classificare hotswap anche quando aggiungi/rimuovi metodi.Freemarker removeIntrospectionInfo non funziona con DCEVM dopo il modello hotswap

Tutto funziona come fascino finché Freemarker utilizza la classe hotswapped come modello. Viene lanciata freemarker.ext.beans.InvalidPropertyException: Nessuna proprietà bean di questo tipo su di me anche se la reflection mostra che il metodo è presente (controllato durante la sessione di debug).

Sto usando

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

per cancellare la cache, ma non funziona. Ho anche provato ad ottenere il campo membro classCache e cancellarlo usando il reflection ma non funziona troppo.

Cosa sto sbagliando? Ho solo bisogno di forzare il freemarker per eliminare qualsiasi introspezione sulle classi/classi di modelli che ha già ottenuto.

C'è qualche modo?

UPDATE

codice Esempio

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!"; 
    } 
} 

Questo esempio non funziona affatto. Anche la modifica di BeansWrapper durante il runtime non avrà alcun effetto.

+0

Supponendo che chiami il metodo su 'BeansWrapper' che è effettivamente in uso, dovrebbe funzionare. Ma 'removeFromClassIntrospectionCache (Class clazz)' è un metodo pubblico, perché lo chiami così? Se si tratta di un vecchio FreeMarker, provare prima un aggiornamento. – ddekany

+0

@ddekany: Sto affrontando lo stesso problema di Martin. Sto usando la versione 2.3.19 quindi l'aggiornamento dovrebbe fare il trucco. Comunque il commento Javadoc per il 'removeFromClassIntrospectionCache' mi fa un puzzle:" '... Se la classe verrà ancora utilizzata, la voce della cache verrà ricreata in silenzio" "- ciò significa che quando una cache del modello contiene il mapping per la classe in questione l'introspezione non otterrebbe la versione ricaricata di esso? Grazie! – plesatejvlk

+0

Sto pulendo l'unico BeansWrapper che ho impostato sull'oggetto configurazione freemarker. Si prega di consultare un aggiornamento con l'esempio. –

risposta

3

BeansWrapper (e DefaultObjectWrapper, ecc.) La cache di introspezione si basa su java.beans.Introspector.getBeanInfo(aClass), non sulla riflessione. (Questo perché tratta gli oggetti come JavaBeans.) java.beans.Introspector ha una propria cache interna, quindi può restituire informazioni obsolete, e in tal caso BeansWrapper semplicemente ricrea i propri dati di introspezione di classe sulla base di tali informazioni obsolete. A partire dal caching di java.beans.Introspector, è in effetti corretto, poiché si basa sul presupposto che le classi in Java siano immutabili. Se qualcosa infrange quella regola di base, dovrebbe assicurare che la cache di java.beans.Introspector sia cancellata (e molte altre cache ...), altrimenti non sarà solo FreeMarker che si interromperà. Ad esempio, per JRebel hanno fatto un grande sforzo per eliminare tutti i tipi di cache. Immagino che DCEVM non abbia le risorse per questo. Quindi, sembra che devi chiamare tu stesso il numero Introspector.flushCaches().

Update: Per un po '(Java 7, forse 6) java.beans.Introspector ha una cache per filo gruppo, in modo da avere chiama flushCaches() da tutti i gruppi di thread. E questo è in realtà un dettaglio di implementazione che, in linea di principio, può cambiare in qualsiasi momento. E purtroppo, il JavaDoc di Introspector.flushCaches() non ti avvisa ...

+0

È interessante, grazie! – plesatejvlk

+0

Ho appena scoperto una cosa davvero strana. Ho implementato questa soluzione per la nostra applicazione che funziona su WLS e ha smesso di funzionare. Ho scoperto che Introspector restituisce un altro valore su theread che viene chiamato da HotSwapAgent e il thread da WLS WorkerThreadPool. Qualche idea per cui sta succedendo e come risolverlo? –

+0

Sospetto cosa sta succedendo ... vedi aggiornamento. – ddekany