Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer JOHANNES KEPLER UNIVERSITY LINZ Research and teaching network
Scripting und Compilation
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 2
Scripting und Compilation
Scripting in der Java VM
Compilation
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 3
Scripting
Ab Java 6 Unterstützung von Script-Sprachen
Einfaches API für die Ausführung von Scripts innerhalb der Java VM
Basissystem für die Implementierung von Script-Engines
Installation von Script-Sprachen über Java Services
Mozilla Rhino JavaScript Engine bereits installiert
package javax.script
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 4
Vorgehen
Erzeugen eines ScriptEngineManagers
Abrufen einer Script-Engine einer bestimmten (installierten) Script-Sprachen über ScriptEngineManager
Ausführen von Scripts
Behandlung von ScriptExceptions
oder Ausführen eines Script-Programms aus einem File (Verwendung eines Readers)
ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName( "JavaScript" ); try { engine.eval( "println('Hello World');" ); } catch (ScriptException e) { System.out.println( "Error in script: line " + e.getLineNumber() + ", colum " + e.getColumnNumber() + ", message " + e.getMessage() ); }
engine.eval(new java.io.FileReader( "SimpleGreeter.js" ));
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 5
Verwendung von Variablen und Bindings
Binden von Variablen mit put
Abrufen von Variablenwerten mit get
Verwendung von Bindings
engine.put( "x" , 1); engine.eval( "x = x + 1;" ); engine.put( "frame" , new JFrame()); engine.eval( "frame.setVisible(true);" );
Object x = engine.get("x"); System.out.println(x); JFrame frame = (JFrame)engine.get( "frame" ); frame.setVisible(false);
Bindings scope = engine.createBindings(); scope.put( "x" , "xxx" ); engine.eval( "x = x + x;" , scope); Object xxx = scope.get( "x" ); System.out.println(xxx);
2
xxxxxx
x = engine.get( "x" ); System.out.println(x); 2
Wert im globalem Scope bleibt erhalten
Evaluierung im Scope
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 6
Funktions- und Methodeaufrufe
Engines müssen Invocable implementieren
Aufruf von Script-Funktionen
Aufruf von Methoden (bei objekt-orientierten Script-Sprachen)
Invocalbe inv = (Invocable) engine;
engine.eval( "function greet(x) {return 'Hello, ' + x + '!'; }" );
String greeting = ((String)inv.invokeFunction( "greet" , "Franz" )); System.out.println(greeting); Hello, Franz!
engine.eval(new FileReader( "SimpleGreeter.js" )); Object goodbyeGreeter = engine.eval( "new SimpleGreeter('Goodbye')" );
System.out.println(inv.invokeMethod(goodbyeGreeter, "greet", "Ann" ));
function SimpleGreeter(salutation) {this.salutation = salutation; } SimpleGreeter.prototype.greet = function(whom) { return this.salutation + ", " + whom + "!" }
"SimpleGreeter.js"
Goodbye, Ann!
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 7
Implementierung von Interfaces
Java-Interfaces können durch Script-Objekte implementiert werden
Mit getInterface(Object, Class<T>) von Invocable
engine.eval( "var obj = new Object(); obj.run = function() { println('run method called'); }" ); Object obj = engine.get( "obj" );
Invocalbe inv = (Invocable) engine; Runnable r = inv.getInterface(obj, Runnable.class); Thread thread = new Thread(r); thread.start();
run method called
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 8
Compilation von Scripts
Scripts können compiliert werden
mit compile von Compilable
Engine muss Compilable implementieren
erzeugt CompiledScript
das mit eval ausgeführt werden kann
Compilable compEngine = (Compilable)engine; CompiledScript script = compEngine.compile( "println ('compiled script called')" ); script.eval();
compiled script called
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 9
Beispiel
Interaktive Java-Script Anwendung
public class JSCalculator { private JTextArea scriptArea; private ScriptEngine jsEngine; privare execBtn; ... public JSCalculator() { ScriptEngineManager manager = new ScriptEngineManager(); jsEngine = manager.getEngineByExtension("js"); frame = new JFrame("JS Calculator"); execBtn = new Jbutton("Exec"); execBtn.addActionListener(execHandler); ... } private ActionListener execHandler = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String script = scriptArea.getText(); Object result; try { result = jsEngine.eval(script); scriptListModel.addElement(script); … scriptArea.setText(" "); } catch (ScriptException e) { resultLabel.setText("Error at column " + e.getColumnNumber() + ": " + e.getMessage()); } catch (Exception e) { resultLabel.setText("Exception occurred: " + e.getMessage()); } } };
TextArea mitJavaScript Code
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 10
Installation von Script-Sprachen
Script-Engine werden als Jar-Libraries installiert
Müssen java.script.ScriptEngineFactory Service implementieren
Registrierung als Service in META-INF
Auffinden
ScriptEngineManager manager = new ScriptEngineManager(); System.out.println( "Available factories: " ); for (ScriptEngineFactory factory : manager.getEngineFactories()) { System.out.println(factory.getEngineName()); } final ScriptEngine engine = manager.getEngineByName( "groovy" ); Object result = engine.eval( "x = 1;" );
Service Discovery!
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 11
Exkurs
Java Services
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 12
Java Service und Service Loader
Implementierungen zur Laufzeit anfordern
Java Service Interface = Spezifikation des Service
Java Service Provider = Implementierung des Service
ServiceLoader = Mechanismus zu Auffinden und Laden der Service
Provider zur Laufzeit
Vorgehen:
Service Interface definieren
Service Provider
implementieren
registieren: in META-INF/services/<full-qualified-servicename>
in Jar-Datei verpacken
Mit ServiceLoader Provider laden
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 13
Beispiel Java Services
Service Interface definieren
Service Provider implementieren
registieren: in META-INF/services/<full-qualified-servicename>
package services; public interface MyService { public void doService(); }
package services; public class FirstProvider implements MyService { @Override public void doService() { System.out.println("First Service"); } }
package services; public class SecondProvider implements MyService { @Override public void doService() { System.out.println(“Second Service"); } }
services.FirstProvider services.SecondProvider
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 14
Beispiel Java Services
Mit ServiceLoader Provider laden
package services; import java.util.ServiceLoader; public class TestMyServices { public static void main(String[] args) { ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class); for (MyService provider: loader) { provider.doService(); } } }
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 15
Scripting und Compilation
Scripting in der Java VM
Compilation
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 16
Compiler Tools API
Zugriff auf Javac-Compiler über Tools API
Ausführen des Java-Compilers durch run
int run(InputStream in, OutputStream out, OutputStream err, String... arguments);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int r = compiler.run(null, null, null, "-sourcepath", ".", "SayHalloClass.java");
Streams für Ein-/Ausgabe (Meldungen etc.) Argumente wie bei Aufruf von javac
Standard Ein-/Ausgabe
0 für Erfolg
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 17
CompilationTasks
CompliationTask erlaubt genaue Kontrolle über Compilation-Prozess
fileManager: Manager für Source- und Class-Files
diagnosticListeners: Behandlung von Fehlermeldungen
classes: Klassen für Annotations-Verarbeitung (hier nicht verwendet)
compilationUnits: Source-Code
Beispiel:
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList( "SayHalloClass.java" )); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits); boolean success = task.call();
CompilationTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options, Iterable<String> classes, Iterable<? extends JavaFileObject> compilationUnits);
Stamdard: Lesen und Schreiben auf Filesystem
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 18
Bsp: Generierung und Compilieren von generiertem Code
Basisklasse mit abstrakter Methode addEventHandlers
Genierierter Code
public abstract class ButtonFrame extends JFrame { public ButtonFrame() {
panel = new JPanel(); yellowButton = new JButton("Yellow"); blueButton = new JButton("Blue"); redButton = new JButton("Red"); ... addEventHandlers(); } protected abstract void addEventHandlers(); … }
yellowButton=panel.setBackground(java.awt.Color.YELLOW); blueButton=panel.setBackground(java.awt.Color.BLUE); redButton=panel.setBackground(java.awt.Color.RED);
Textfile action.properties mit Action-Code
für Buttons
package x; public class Frame extends com.horstmann.corejava.ButtonFrame { protected void addEventHandlers() { yellowButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent event) { panel.setBackground(java.awt.Color.YELLOW); } } ); redButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent event) { panel.setBackground(java.awt.Color.RED); } } ); blueButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent event) { panel.setBackground(java.awt.Color.BLUE); } } ); } }
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 19
Bsp: Generierung und Compilieren von generiertem Code
1. Source-Code wird mit StringBuilder dynamisch erzeugt
2. Byte-Code wird in Byte-Array abgelegt
3. Code wird aus Byte-Array geladen und ausgeführt
Ad 1.) JavaFileObject mit StringBuilder als Quelle
public class StringBuilderJavaSource extends SimpleJavaFileObject { public StringBuilderJavaSource(String name) { super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); code = new StringBuilder(); } public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; } public void append(String str) { code.append(str); code.append('\n'); } private StringBuilder code; }
Name des „FileObjects“
liefert Source-Code
für Erzeugung des Source-Codes
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 20
Beispiel: Generierung und Compilieren von Code
Ad 1.) Erzeugen des Source-Codes aus Template
static JavaFileObject buildSource(String superclassName) throws IOException { StringBuilderJavaSource source = new StringBuilderJavaSource("x.Frame"); source.append("package x;\n"); source.append("public class Frame extends " + superclassName + " {"); source.append("protected void addEventHandlers() {"); Properties props = new Properties(); props.load(new FileReader("action.properties")); for (Map.Entry<Object, Object> e : props.entrySet()) { String beanName = (String) e.getKey(); String eventCode = (String) e.getValue(); source.append(" " + beanName + ".addActionListener(new java.awt.event.ActionListener() {"); source.append(" public void actionPerformed(java.awt.event.ActionEvent event) {"); source.append(" " + eventCode); source.append(" } } );"); } source.append("} }"); return source; } }
Event Code aus Datei action.properties wird in
actionPerformed von ActionsListener verpackt und
bei Komponenten angefügt!
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 21
Beispiel: Generierung und Compilieren von Code
Ad 2.) JavaFileObject mit ByteArrayOutputStream für Byte-Code
public class ByteArrayJavaClass extends SimpleJavaFileObject { public ByteArrayJavaClass(String name) { super(URI.create("bytes:///" + name), Kind.CLASS); stream = new ByteArrayOutputStream(); } public OutputStream openOutputStream() throws IOException { return stream; } public byte[] getBytes() { return stream.toByteArray(); } private ByteArrayOutputStream stream; }
OutputStream für Compiler
liefert Byte-Code
Name des „FileObjects“
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 22
Beispiel: Generierung und Compilieren von Code
Ad 2.) Erzeugen eines FileManagers, der entsprechende FileObjects
liefert
ForwardingJavaFileManager
JavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); fileManager = new ForwardingJavaFileManager<JavaFileManager>(fileManager) { public JavaFileObject getJavaFileForOutput(Location location, final String className, Kind kind, FileObject sibling) throws IOException { if (className.startsWith("x.")) { ByteArrayJavaClass fileObject = new ByteArrayJavaClass(className);
classFileObjects.add(fileObject); return fileObject;
} else { return super.getJavaFileForOutput(location, className, kind, sibling);
} } };
Delegation aller nicht-
überschriebenen Aufrufe
zu diesem FileManager
Liefert ByteArrayJavaClass für alle Klassen im Package "x"
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 23
Beispiel: Generierung und Compilieren von Code
Ad 3.) Compilation
Laden
Ausführen (über Reflection)
JavaFileObject source = buildSource("com.horstmann.corejava.ButtonFrame"); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, Arrays.asList(source)); Boolean result = task.call();
Map<String, byte[]> byteCodeMap = new HashMap<String, byte[]>(); for (ByteArrayJavaClass cl : classFileObjects) { byteCodeMap.put(cl.getName().substring(1), cl.getBytes()); } ClassLoader loader = new MapClassLoader(byteCodeMap); Class<?> cl = loader.loadClass("x.Frame");
JFrame frame = (JFrame) cl.newInstance(); frame.setTitle("CompilerTest"); frame.setVisible(true);
public class MapClassLoader extends ClassLoader { public MapClassLoader(Map<String, byte[]> classes) { this.classes = classes; } protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classBytes = classes.get(name); if (classBytes == null) throw new ClassNotFoundException(name); Class<?> cl = defineClass(name, classBytes, 0, classBytes.length); if (cl == null) throw new ClassNotFoundException(name); return cl; } private Map<String, byte[]> classes; }
Pratikum SWE 2 © M. Löberbauer, T. Kotzmann, H. Prähofer, Institut für Systemsoftware, Johannes Kepler Universität Linz 24
Zusammenfassung
Scripting API erlaubt Verwendung von Script-Sprachen in Java-
Anwendungen
Compiler Tool API erlaubt Verwendung des Java-Compilers in Java-
Anwendungen
Java Service API wird zum Auffinden von installierten Script-Sprachen
und Compilern verwendet