Java Database Connectivity-API (JDBC)

Werbung
Scripting und Compilation
JOHANNES KEPLER UNIVERSITY LINZ
Research and teaching network
Pratikum SWE 2
© M. Löberbauer, T. Kotzmann, H. Prähofer
Scripting und Compilation
Scripting in der Java VM
Compilation
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
Vorgehen
 Erzeugen eines ScriptEngineManagers
 Abrufen einer Script-Engine einer bestimmten (installierten) ScriptSprachen über ScriptEngineManager
 Ausführen von Scripts
 Behandlung von ScriptExceptions
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() );
}
 oder Ausführen eines Script-Programms aus einem File (Verwendung
eines Readers)
engine.eval(new java.io.FileReader( "SimpleGreeter.js" ));
Verwendung von Variablen und Bindings
Binden von Variablen mit put
engine.put( "x" , 1);
engine.eval( "x = x + 1;" );
engine.put( "frame" , new JFrame());
engine.eval( "frame.setVisible(true);" );
Abrufen von Variablenwerten mit get
Object x = engine.get("x");
System.out.println(x);
JFrame frame = (JFrame)engine.get( "frame" );
frame.setVisible(false);
2
Verwendung von Bindings
Bindings scope = engine.createBindings();
scope.put( "x" , "xxx" );
engine.eval( "x = x + x;" , scope);
Object xxx = scope.get( "x" );
System.out.println(xxx);
Evaluierung im Scope
x = engine.get( "x" );
System.out.println(x);
xxxxxx
2
Wert im globalem Scope bleibt erhalten
Funktions- und Methodeaufrufe
Engines müssen Invocable implementieren
Invocalbe inv = (Invocable) engine;
Aufruf von Script-Funktionen
engine.eval( "function greet(x) {return 'Hello, ' + x + '!'; }" );
String greeting = ((String)inv.invokeFunction( "greet" , "Franz" ));
System.out.println(greeting);
Hello, Franz!
Aufruf von Methoden (bei objekt-orientierten Script-Sprachen)
engine.eval(new FileReader( "SimpleGreeter.js" ));
Object goodbyeGreeter = engine.eval( "new SimpleGreeter('Goodbye')" );
"SimpleGreeter.js"
function SimpleGreeter(salutation) {this.salutation = salutation; }
SimpleGreeter.prototype.greet =
function(whom) { return this.salutation + ", " + whom + "!" }
System.out.println(inv.invokeMethod(goodbyeGreeter, "greet", "Ann" ));
Goodbye, Ann!
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
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
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);
...
}
TextArea mitJavaScript Code
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());
}
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());
}
Service Discovery!
final ScriptEngine engine = manager.getEngineByName( "groovy" );
Object result = engine.eval( "x = 1;" );
Exkurs
Java Services
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
Beispiel Java Services
Service Interface definieren
package services;
public interface MyService {
public void doService();
}
Service Provider implementieren
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");
}
}
registieren: in META-INF/services/<full-qualified-servicename>
services.FirstProvider
services.SecondProvider
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();
}
}
}
Scripting und Compilation
Scripting in der Java VM
Compilation
Compiler Tools API
Zugriff auf Javac-Compiler über Tools API
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
Ausführen des Java-Compilers durch run
0 für Erfolg
Streams für Ein-/Ausgabe (Meldungen etc.)
Argumente wie bei Aufruf von javac
int run(InputStream in, OutputStream out, OutputStream err, String... arguments);
Standard Ein-/Ausgabe
int r = compiler.run(null, null, null, "-sourcepath", ".", "SayHalloClass.java");
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
CompilationTask getTask(Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits);
Beispiel:
Stamdard: Lesen und Schreiben auf Filesystem
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();
Bsp: Generierung und Compilieren von generiertem Code
Basisklasse mit abstrakter Methode addEventHandlers
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();
…
}
Textfile action.properties mit Action-Code
für Buttons
yellowButton=panel.setBackground(java.awt.Color.YELLOW);
blueButton=panel.setBackground(java.awt.Color.BLUE);
redButton=panel.setBackground(java.awt.Color.RED);
Genierierter Code
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);
} } );
} }
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();
}
Name des „FileObjects“
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
public void append(String str) {
code.append(str);
code.append('\n');
}
private StringBuilder code;
}
liefert Source-Code
für Erzeugung des Source-Codes
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!
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;
}
Name des „FileObjects“
OutputStream für Compiler
public byte[] getBytes() {
return stream.toByteArray();
}
private ByteArrayOutputStream stream;
}
liefert Byte-Code
Beispiel: Generierung und Compilieren von Code
Ad 2.) Erzeugen eines FileManagers, der entsprechende FileObjects
liefert
 ForwardingJavaFileManager
Delegation aller nichtüberschriebenen Aufrufe
zu diesem FileManager
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);
}
}
};
Liefert ByteArrayJavaClass
für alle Klassen im Package "x"
Beispiel: Generierung und Compilieren von Code
Ad 3.) Compilation
JavaFileObject source = buildSource("com.horstmann.corejava.ButtonFrame");
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null,
null, Arrays.asList(source));
Boolean result = task.call();
Laden
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");
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;
}
Ausführen (über Reflection)
JFrame frame = (JFrame) cl.newInstance();
frame.setTitle("CompilerTest");
frame.setVisible(true);
private Map<String, byte[]> classes;
}
Zusammenfassung
Scripting API erlaubt Verwendung von Script-Sprachen in JavaAnwendungen
Compiler Tool API erlaubt Verwendung des Java-Compilers in JavaAnwendungen
Java Service API wird zum Auffinden von installierten Script-Sprachen
und Compilern verwendet
Herunterladen