2014-12-06 17 views
10

Sto sviluppando un motore di gioco basato su componenti in java, proprio ora quando ho delle modifiche ai componenti che ho bisogno di ricostruire e riavviare l'editor per rendere effettive le modifiche (o posso usare un codice a caldo limitato iniezione se l'applicazione è in esecuzione in modalità di debug).Come integrare beanshell

Sto cercando un modo per consentire all'utente di modificare la fonte dei componenti e ricaricarli senza dover riavviare l'applicazione (magari basta uscire e accedere invece alla modalità di gioco). Inoltre, una caratteristica importante di cui ho bisogno è che il codice finale esportato dovrebbe essere codice Java nativo (quindi non dovrebbe essere usato nessun interprete nel risultato finale)).

Potete darmi qualche suggerimento su come integrare l'interprete di beanshell nel progetto? Posso monitorare manualmente la cartella di origine per le modifiche e inserire le classi java aggiornate su di essa, ma come si verificherà realmente l'hotswap?

+0

http://www.beanshell.org/manual/embeddedmode.html –

+0

Mi chiedo se nessuno abbia menzionato OSGi. http://en.wikipedia.org/wiki/OSGi – Yser

risposta

4

Prima di tutto, il titolo è un po 'confuso. Non è necessario integrare un BeanShell. Quello che realmente bisogno sono:

  1. per definire un'architettura corretta
  2. usare Java Compiler API al fine di lavorare con le classi Java

Architettura

Diciamo che avete un grafico dell'oggetto. Ci sono molti oggetti, riferimenti, ecc. Quindi sarà davvero un compito complicato sostituire qualche istanza con quella nuova. Invece di risolvere questo problema, puoi nascondere la parte dinamica dietro un proxy "statico". Proxy gestirà tutto il roba di ricarica (incluso il monitoraggio della cartella sorgente).

Prima ricarica:

enter image description here

Dopo ricarica:

enter image description here

Avere Fatto questo si può facilmente tenere traccia delle modifiche e aggiornare parte dinamica quando necessario.

Java Compiler API

Invece di usare linguaggi interpretati è possibile utilizzare Java, compilarlo al volo e carico con 'Class.forName()'. Ci sono molti esempi diversi a causa del fatto che questo approccio è stato lì per un po '.

Ecco alcuni dettagli:

+0

La mia domanda era molto specifica, come integrare * beanshell * per fare lo swap a caldo, ma nessuno ha risposto. Non voglio ricompilare manualmente il codice e gestire manualmente il ricaricamento dinamico, vale anche la pena notare che avere un proxy tra le parti dinamiche e statiche è un no-go in quanto comporta un overhead aggiuntivo sui dispositivi finali (che includono alcuni vecchi dispositivi j2me). –

+0

La tua risposta sembra la più completa per le altre persone con problemi simili, ecco perché la seleziono per la taglia. –

0

Fondamentalmente si desidera implementare extensibility o plug-in design pattern. Esistono diversi modi per implementare questo scenario.

Quale che sia il componente che si desidera consentire a qualcun altro di ricaricare il modulo, definire un'interfaccia e implementare la propria implementazione come predefinita.Ad esempio, qui sto cercando di fornire una HelloInterface che ogni paese può realizzare e caricare in qualsiasi momento,

public interface HelloInterface { 

    public String sayHello(String input); 
    .. 
} 

public class HelloImplDefault implements HelloInterface { 

    public String sayHello(String input) { 
     return "Hello World"; 
    } 
} 

Ora consentire all'utente di aggiungere un plugin (un'implementazione personalizzata) i file in un percorso pre-configurato. È possibile utilizzare FileSystemWatcher o un thread in background per analizzare questo percorso e provare a compilare e caricare il file.

Per compilare il file java,

private void compile(List<File> files) throws IOException{ 


     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
     DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 
     StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); 
     Iterable<? extends JavaFileObject> compilationUnits = fileManager 
      .getJavaFileObjectsFromFiles(files); 
     JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, 
      null, compilationUnits); 
     boolean success = task.call(); 
     fileManager.close(); 

} 

Per caricare file di classe,

private void load(List<File> files) throws MalformedURLException, InstantiationException, IllegalAccessException, ClassNotFoundException{ 

    ClassLoader cl = Thread.currentThread().getContextClassLoader(); 

    for(File f: files){ 
     if(f.getName().endsWith(".class") && !loadedClass.contains(f.getName())){ 
      URL url = f.toURL(); 
      URL[] urls = new URL[]{url}; 
      Object obj = cl.loadClass(f.getName().replace(".class", "")).newInstance(); 
      if(obj instanceof HelloInterface){ 
       HelloProviders.addProvider((HelloInterface)obj); 
       System.out.println("Loaded "+ ((HelloInterface)obj).getProviderName()); 
      }else{ 
       //Add more classes if you want 
      } 
      loadedClass.add(f.getName()); 
     } 
    } 
} 

A questo punto è possibile leggere un'implementazione personalizzata e caricati in classe sistema di caricamento. Ora sei pronto per andare. Ci sono implicazioni sulla sicurezza di questo approccio che devi imparare da internet.

Ho implementato un codice di esempio e pubblicato in github, please take a look. Buona programmazione!

0

Dai un'occhiata all'inversione tapestry-ioc del contenitore di controllo che supporta live-reloading.

In modalità sviluppo (tapestry.production-mode = false) puoi vivere ricaricando i tuoi servizi. Si noti che se viene modificata un'interfaccia di servizio, è necessario il . Tuttavia, eventuali modifiche all'implementazione del servizio che non alterano l'interfaccia del servizio possono essere ripristinate in tempo reale.