2014-12-06 3 views
10
integriert

Ich entwickle eine komponentenbasierte Spielengine in Java, gerade jetzt, wenn ich Änderungen an den Komponenten habe, die ich neu aufbauen und den Editor neu starten muss, damit die Änderungen wirksam werden (oder ich kann begrenzten Hotcode verwenden) Injektion, wenn die Anwendung im Debug-Modus ausgeführt wird).Wie man Bohnenschale

Ich bin auf der Suche nach einer Möglichkeit, um den Benutzer die Quelle der Komponenten zu ändern und neu zu laden, ohne die Anwendung neu starten zu müssen (vielleicht nur den Spielmodus verlassen und stattdessen eingeben). Eine wichtige Funktion, die ich brauche, ist, dass der fertige exportierte Code nativer Java-Code sein sollte (also sollte kein Interpreter im Endergebnis verwendet werden)).

Können Sie mir irgendwelche Hinweise geben, wie Sie den Beanshell-Interpreter in das Projekt integrieren können? Ich kann den Quellordner manuell auf Änderungen überwachen und ihm die aktualisierten Java-Klassen zuführen, aber wie wird der Hotswap wirklich ausgeführt?

+0

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

+0

Ich frage mich, niemand erwähnte OSGi. http://en.wikipedia.org/wiki/OSGi – Yser

Antwort

4

Zunächst ist der Titel ein wenig verwirrend. Sie müssen keine BeanShell integrieren. Was Sie wirklich brauchen, sind:

  1. eine richtige Architektur
  2. zu definieren, verwenden Java Compiler API, um mit Java-Klassen zu arbeiten

Architektur

Lasst uns sagen, dass Sie eine haben Objektgraph. Es gibt viele Objekte, Referenzen usw. Es wird also eine schwierige Aufgabe sein, eine Instanz durch die neue zu ersetzen. Anstatt dieses Problem zu lösen, können Sie den dynamischen Teil hinter einem "statischen" Proxy verbergen. Proxy kümmert sich um das Nachladen (einschließlich der Überwachung von Quellordnern).

Vor reload:

enter image description here

Nach reload:

enter image description here

das getan zu haben Sie leicht Änderungen verfolgen und dynamischen Teil zu aktualisieren, wenn nötig.

Java Compiler API

Stattdessen interpretiert Sprachen verwenden Sie Java verwenden können, es on the fly und Laden Kompilieren mit 'Class.forName()'. Es gibt viele verschiedene Beispiele aufgrund der Tatsache, dass dieser Ansatz für eine Weile da war.

Hier einige Details:

+0

Meine Frage war sehr spezifisch, wie man * beanshell * integriert, um das heiße Tauschen zu tun, gut niemand hat es geantwortet. Ich möchte den Code nicht manuell neu kompilieren und das dynamische Neuladen manuell verwalten. Bemerkenswert ist auch, dass ein Proxy zwischen den dynamischen und statischen Teilen ein Nein ist, da dies zusätzlichen Aufwand für die letzten Geräte mit sich bringen wird j2me-Geräte). –

+0

Ihre Antwort sieht am vollständigsten für andere Leute mit ähnlichen Problemen aus, deshalb wähle ich sie für das Kopfgeld aus. –

0

Grundsätzlich Sie extensibility oder Plugin-Design-Pattern implementieren möchten. Es gibt mehrere Möglichkeiten, dieses Szenario zu implementieren.

Jeweils die Komponente, die jemand anderem erlauben soll, das Modul neu zu laden, eine Schnittstelle definieren und eine eigene Implementierung als Standard implementieren.Zum Beispiel, hier versuche ich, eine HelloInterface zu schaffen, das jedes Land kann jederzeit implementieren und laden,

public interface HelloInterface { 

    public String sayHello(String input); 
    .. 
} 

public class HelloImplDefault implements HelloInterface { 

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

Jetzt Benutzer erlauben, ein Plugin (benutzerdefinierte Implementierung) Dateien zu einem vorkonfigurierten Pfad hinzuzufügen. Sie können entweder FileSystemWatcher oder einen Hintergrund-Thread verwenden, um diesen Pfad zu scannen und zu versuchen, die Datei zu kompilieren und zu laden.

Java-Datei zu kompilieren,

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(); 

} 

Klassendatei zu laden,

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()); 
     } 
    } 
} 

An dieser Stelle Sie benutzerdefinierte Implementierung und Systemklassenlader geladen lesen kann. Jetzt sind Sie bereit zu gehen. Dieser Ansatz hat Sicherheitsaspekte, die Sie aus dem Internet lernen müssen.

Ich habe einen Beispielcode implementiert und in GitHub, please take a look. Glückliche Kodierung!

0

Werfen Sie einen Blick auf die tapestry-ioc Inversion des Steuercontainers, die unterstützt.

Im Entwicklungsmodus (tapstry.production-mode = false) können Sie Ihre Dienste neu laden. Beachten Sie, dass Sie, wenn sich eine Service-Schnittstelle ändert, einen Neustart benötigen. Änderungen an der Service-Implementierung, die die Service-Schnittstelle nicht verändern, können jedoch live geladen werden.