1. Plux.NET
Software-Komposition durch Plug & Play
Reinhard Wolfinger
Markus Jahn
Hanspeter Mössenböck
Johannes Kepler Universität Linz
Institut für Systemsoftware
Christian Doppler Labor für Automated Software Engineering
http://ase.jku.at/plux/
1
2. Motivation
Die meisten Programme sind heute monolithisch
z.B.: MS Word
• groß
• komplex
• teuer
• kleine Änderungen erfordern
Auslieferung des ganzen Systems
• schwer erweiterbar durch andere
Firmen und Endbenutzer
• ...
90% der Funktionalität kaum benutzt
50% der Funktionalität nie benutzt
2
3. Plugin-Ansatz
Statt eines Monolithen ... ... ein schlanker Kern plus Plugins
Voucher Dunning
Voucher
Dunning
Balance Sheet
Budget Balance Sheet
Customer Data
Debit Customer Data
Debit Costing Core
Ziele
• Jeder Benutzer lädt nur das, was er braucht
• Dynamisches Hinzufügen von Plugins ohne Neustart der Applikation
• Dynamisches Entfernen von Plugins ohne Neustart
• Dynamischer Umbau der Applikation für verschiedene Benutzungsszenarien
• Einfache Erweiterbarkeit (Plug & Play) ohne programmieren oder konfigurieren
• Keine XML-Konfigurationsdateien
• Sichere Erweiterbarkeit (Wer kann ein Plugin hinzufügen? Was darf ein Plugin tun?)
Plux.NET 3
4. Grundkonzepte
Extensions, Slots, Plugs
Slot Plug
Extensions
(Klassen)
Plugin ⇒ .NET-Assembly (dll-Datei)
Slots und Plugs im Detail
Host Extension
Interface Slot Plug Implementierung dieses Interface
Name Wert
Parameter
Name Wert
4
5. Beschreibung der Komposition
Zuerst müssen Slots definiert werden ...
[SlotDefinition("MenuItem")] MenuItem
[Param("Name", typeof(string))] Metadaten durch .NET-Attribute beschrieben
[Param("Icon", typeof(string))]
Name
interface IMenuItem {
void DoMenuCommand(...); Icon
}
Extensions können Slots öffnen ... ... und sich über Plugs verbinden
Host PrintItem
[Extension("Host")] [Extension("PrintItem")]
[Slot("MenuItem")] [Plug("MenuItem")]
class Host { MenuItem MenuItem [ParamValue("Name", "Print")]
... [ParamValue("Icon", "Print.gif")]
} Name "Print" class PrintItem: IMenuItem {
...
Icon "Print.gif" }
Slot Plug
⇒ keine extra XML-Dateien
⇒ Plugins enthalten Code und Metadaten
5
6. Automatische Komposition
Plugin Repository
(Filesystem-Verzeichnis)
D.dll E.dll
A D D E
S1 S3
S2 S2
C.dll
S4
... S2 C
B F.dll S1 S2
S1 F C
S5 S1
• Benutzer schiebt C.dll ins Repository
• Runtime sucht nach offenen Slots in die C gesteckt werden kann (=> S1)
• Runtime steckt C in Slots S1 von A und B
• Runtime öffnet die Slots von C (=> S2)
• Runtime durchsucht das Repository nach passenden Extensions (=> D)
• Runtime steckt D in Slot S2 von C
• Runtime öffnet Slots von D
• ...
Plugins können dynamisch ausgesteckt werden, indem C.dll aus dem Repository entfernt wird
6
7. Discovery ist selbst ein Plugin
Applikation
Runtime Core
Startup
Discovery
Discovery
Manager
~ 200 KB
Standard-Discovery-Mechanismus
• Discovery aus einem Verzeichnis des Dateisystems
kann ersetzt werden, z.B. durch
• Discovery aus mehreren Verzeichnissen
• Discovery über das Web
• Discovery mit einem Benutzerdialog
• ...
7
8. Ereignisgesteuerte Komposition
Plugged Extension wurde angesteckt Opened Slot wurde geöffnet ...
Unplugged Extension wurde abgesteckt Closed Slot wurde geschlossen
Host Plugged ConsoleLogger
[SlotDefinition("Logger")]
[Param("TimeFormat", typeof(string))] Logger Logger
interface ILogger { (TimeFormat) (TimeFormat = "hh:mm:ss")
void Print(string s);
}
[Extension("Host")] [Extension("ConsoleLogger")]
[Slot("Logger", OnPlugged="AddLogger")] [Plug("Logger")]
class Host { [ParamValue("TimeFormat", "hh:mm:ss")]
Logger logger = null; class ConsoleLogger : ILogger {
string format; void Print(string s) {
void AddLogger(object sender, PlugEventArgs args) { Console.WriteLine(s);
logger = (ILogger) args.Extension; }
format = (string) args.GetParam("TimeFormat"); }
}
void Run() {
... Der Host kann eine Extension ablehnen
if (logger != null)
logger.Print(DateTime.Now.ToString(format));
(auf Grund interner Prüfungen)
}
} 8
9. Kardinalität und Sharing
Kardinalität von Slots
[Extension("Host")] [Extension("Host")]
[Slot("S")] [Slot("S", Multiple=true)]
Extensions
Host Extension Host
S
S S S
(Multiple) S
1:1 1:n
Sharing von Extensions
[Extension("Host")] [Extension("Host")]
[Slot("S")] [Slot("S", Unique=true)]
Host Extension Host Extension
S S S S
(Shared) (Shared) (Unique) (Unique)
S S S
(Shared) (Unique) (Unique)
9
10. Lazy Loading
Nicht alle Extensions müssen am Programmbeginn geladen werden => schnellere Startzeiten
Host Registered ConsoleLogger
Logger Logger noch nicht geladen,
(LazyLoad) aber Metadaten sind schon verfügbar
[Extension("Host")]
[Slot("Logger", LazyLoad=true, OnRegistered="RegisterLogger", ...]
class Host {
ILogger logger = null;
string format;
void RegisterLogger(object sender, RegisterEventArgs args) {
format = (string) args.GetParam("TimeFormat");
}
...
}
Host kann den TimeFormat-Parameter schon benutzen, um Log-Nachrichten vorzubereiten.
Wenn er den Logger braucht, muss er ihn manuell laden:
Runtime.GetSlot(this, "Logger").PlugAllRegistered(); // raises Plugged event
10
11. Kompositionslgorithmus
H E
S P
Open slot H.S: Plug E.P into H.S
for all known extensions E Raise Registered event at H.S
for all plugs P of E fitting into H.S if (H.S.LazyLoad) return;
Plug E.P into H.S if (E not yet loaded) Load E
Instantiate E
Raise Plugged event at H.S
for all slots S of E
Open slot E.S
Bis alles eingesteckt ist, was eingesteckt werden kann.
Automatische Komposition kann durch manuelle Komposition überschrieben werden
(für fortgeschrittene Benutzer)
11
12. Kompositionszustand
Wird zur Laufzeit in Metaobjekten gespeichert
PrintItem
Data
Host Data
MenuItem Metaobjekte
SaveItem
Data
Host PrintItem SaveItem Data
object object object object
Objekte
Metadaten
• Welche Extensions liegen im Plugin-Repository?
• Welche Extensions sind momentan geladen?
• Welche Plugs stecken in einem bestimmten Slot?
• In welchen Slots ist ein bestimmter Plug angesteckt?
• Welche Slots sind momentan offen?
• ...
12
14. Sicherheitsmechanismen
Basierend auf den Sicherheitsmechanismen von .NET
Von wem kommt die => Signed Assemblies (Public-Key-Kryptographie)
Extension?
assembly
Dateien Identität
private key signed assembly
Validierung
public key
der Unterschrift
✔|✖
Was darf die Extension => Permission Sets (können durch Administrator definiert werden)
tun?
• permission A
Permission- • permission B
Set-Name • permission C
• ...
14
15. Wer darf sich in einen Slot anstecken?
Standard nur Extensions mit derselben Identität wie der Host und seine Vorgänger
E0 E1
[Extension("E1")] id1
S0 S1
[Slot("S1")] id0 id1
id0
Rechte können erweitert werden
[Extension("E1")]
[Slot("S1")] id2
[AllowPlug("id2.key")]
[AllowPlug("id3.key", PermissionSet="Sandbox")] id3 Sandbox-Permissions
Name der Name des
Public-Key-Datei Permission Sets
[Extension("E1")]
[Slot("S1")] id4
[AllowPlug ("*")]
Rechte können in Slot-Definition vordefiniert werden
[SlotDefinition("S1")]
[AllowPlug("id2.key")]
15
16. Wer darf einen Slot öffnen?
Warum sollte nicht jeder einen Slot öffnen dürfen?
Sicherheitskritische Plugins sollten nicht in unbekannte Hosts eingesteckt werden
Standard nur Extensions mit derselben Identität wie die Slot-Definition
[SlotDefinition("S")] wenn die Slot-Definition Identität id0 hat,
dürfen nur Extensions mit Identität id0 diesen Slot öffnen
Rechte können erweitert werden
[SlotDefinition("S")]
[AllowOpen("id1.key")]
[AllowOpen("id2.key", PermissionSet="Sandbox")]
[SlotDefinition("S")]
[AllowOpen("*")]
16
17. Security Manager
Ist ebenfalls ein Plugin => optional, nicht hartcodiert in der Runtime
Plugged
Applikation
Runtime Core
Startup
Security
Manager
Discovery
Discovery
Manager
• Security Manager wird sofort eingesteckt (kann nur beim Start ausgetauscht werden)
• Kontrolliert das Öffnen und Pluggen für alle anderen Slots
- fängt Opened- und Plugged-Ereignisse ab
• Wenn Security Manager fehlt, werden keine Rechte überprüft
17
18. Manuelle Komposition
Runtime bietet API für
• Öffnen und Schließen von Slots
• An- und Abstecken von Extensions
• Iterieren über alle Slots/Plugs einer Extension
• Für einen bestimmten Slot: welche Plugs stecken in ihm?
• Für einen bestimmten Plug: in welchen Slots steckt er?
• ...
[Extension("E")]
[Slot("S", AutoOpen=false)]
muss durch E oder eine andere Extension
manuell geöffnet werden
Benutzungsszenarien
• einen Slot nur unter bestimmten Bedingungen öffnen
• eine Extension in einem Slot manuell durch eine andere ersetzen
• ...
Skript-Sprache verfügbar
• um Anwendung in einer bestimmten Konfiguration zu starten
• um eine Anwendung dynamisch umzukonfigurieren
• um die momentane Konfiguration für Fehlerberichte aufzuzeichnen
• ... 18
19. Plux versus Eclipse
Eclipse bietet weniger Support für automatische Komposition
• Komposition muss manuell codiert werden (nicht Teil des Eclipse-Frameworks)
• Unterschiedliche APIs für die Komposition zur Startzeit und zur Laufzeit
19
20. Kompositionscode in Eclipse
Komposition zur Startzeit
// code in the host host extension
get extension point info from Eclipse registry
extension
for all extensions in registry that can plug into this extension point
point
get metadata of the extension
read parameters from metadata <extension ...>
read class name from metadata class="..."
create an instance of this class param="..."
</extension>
integrate the instance into the host
nur diese beiden Dinge sind in Plux nötig
Komposition zur Laufzeit
Event-Handler sind auch in Plux nötig
// code in the host
aber mit weniger Codieraufwand
write a listener for the extension point
(using similar code as above)
• Kein Inspizieren der Metadaten
• Objekte müssen nicht manuell erzeugt werden
register the listener in the Eclipse registry In Plux nicht nötig
20
21. Plux versus Eclipse
Eclipse bietet weniger Support für automatische Komposition
• Komposition muss manuell codiert werden (nicht Teil des Eclipse-Frameworks)
• Unterschiedliche APIs für die Komposition zur Startzeit und zur Laufzeit
• Lazy loading, Kardinalitäten, Extension Sharing, etc. müssen manuell codiert werden
• Keine Laufzeitinformation über den Kompositionszustand
• Keine eingebauten Scherheitsmechanismen für das Plugging
• Konfiguration muss in separaten XML-Dateien beschrieben werden
• Es gibt zwar Wizards, aber die machen viel hinter den Kulissen
- erzeugen XML-Dateien
- erzeugen Activator-Klassen
- erzeugen Bundles (Jar-Dateien mit Manifest)
21
22. Plux.NET Zusammenfassung
• Leichtgewichtige Plugin-Plattform für .NET
• "Slot & Plug" Metapher
• Deklarative Komposition (durch .NET-Attribute)
• Komponenten sind in sich abgeschlossen (keine separaten XML-Dateien)
• Dynamisches Anstecken / Abstecken / Umkonfigurieren
• Kompositionsmechanismen sind bereits Teil der Plattform
• Lazy Loading, Kardinalitäten von Slots, Shared Extensions
• Kompositionszustand zur Laufzeit abfragbar
• Sicherheitsmechanismen (Wer darf sich anstecken? Welche Rechte?)
• Manuelle Komposition und Scripting möglich
Zukünftige Arbeiten
• Ausbau von Plux für Web-Applicationen
• Testen von Plugin-Systemen
http://ase.jku.at/plux/
22