2015-04-11 7 views
9

Ich habe eine Multi-Modul SBT Build bestehend aus api, core und third-party. Die Struktur ist in etwa so:Mit einem benutzerdefinierten Klassenlader für eine Modulabhängigkeit in SBT

api 
|- core 
|- third-party 

Der Code für third-party implementiert api und ist wörtlich von woanders kopiert, so ich will es nicht wirklich berühren.

Wegen der Art und Weise third-party implementiert ist (starker Einsatz von Singletons), kann ich nicht einfach core von third-party abhängen. Insbesondere muss ich es nur über die api verwenden, aber ich muss mehrere, isolierte Kopien von third-party zur Laufzeit haben. (. Dies ermöglicht es mir zugleich mehrere „Singletons“ zu haben)

Wenn ich außerhalb meines SBT Build ausgeführt wird, ich dies nur tun:

def createInstance(): foo.bar.API = { 
    val loader = new java.net.URLClassLoader("path/to/third-party.jar", parent) 
    loader.loadClass("foo.bar.Impl").asSubclass(classOf[foo.bar.API]).newInstance() 
} 

Aber das Problem ist, dass ich don Ich weiß nicht, wie ich zur Laufzeit herausfinden soll, was ich als Argument zu URLClassLoader geben sollte, wenn ich über sbt core/run laufe.

+1

1. Wie beabsichtigen Sie, das Projekt zu bündeln? SBT-Montage? Oder wird das Third-Party.jar extern sein? 2. Welche Form haben die Drittanbieter-Singletons? Objekt? Ich frage, weil Sie erwähnen .newInstance() –

+0

@DaleWijnand 1. Noch nicht entschieden, aber es ist wahrscheinlich, dass ich 'sbt-Assembly' Bundle 'API', 'Kern' und die Scala-Bibliothek und' Drittanbieter .jar' getrennt. – larsrh

+0

@DaleWijnand 2. Es ist ziemlich kompliziert. Grundsätzlich verwaltet die Third-Party-Bibliothek einige externe Prozesse. Es kann viele dieser Prozesse gleichzeitig verarbeiten, muss jedoch initialisiert werden. Bei der Initialisierung füllt er eine Karte innerhalb eines Objekts (d. H. Ein literales Scala-Objekt), z. der Weg zum externen Prozess. Mein Code muss in der Lage sein, mit mehreren Speicherorten dieses externen Prozesses umzugehen. – larsrh

Antwort

4

Das sollte funktionieren, obwohl ich es nicht mit Ihrer Einrichtung getestet habe.

Die Grundidee ist es, sbt schreiben den Klassenpfad in eine Datei, die Sie zur Laufzeit verwenden können. sbt-buildinfo bietet bereits eine gute Grundlage dafür, also werde ich es hier verwenden, aber Sie könnte nur den relevanten Teil extrahieren und dieses Plugin auch nicht verwenden.

Fügen Sie diese auf Ihre Projektdefinition:

lazy val core = project enablePlugins BuildInfoPlugin settings (
    buildInfoKeys := Seq(BuildInfoKey.map(exportedProducts in (`third-party`, Runtime)) { 
    case (_, classFiles) ⇒ ("thirdParty", classFiles.map(_.data.toURI.toURL)) 
    }) 
    ... 

Zur Laufzeit verwenden diese:

def createInstance(): foo.bar.API = { 
    val loader = new java.net.URLClassLoader(buildinfo.BuildInfo.thirdParty.toArray, parent) 
    loader.loadClass("foo.bar.Impl").asSubclass(classOf[foo.bar.API]).newInstance() 
} 

exportedProducts enthält nur die kompilierten Klassen für das Projekt (z .../target/scala-2.10/classes/). Abhängig von Ihrer Konfiguration möchten Sie möglicherweise fullClasspath anstelle von (das auch die libraryDependencies und abhängigen Projekte enthält) oder einen anderen mit dem Klassenpfad verbundenen Schlüssel verwenden.

+0

Das klingt großartig, ich werde es ausprobieren. – larsrh

+0

Entschuldigung für die späte Antwort. Ihre Lösung hat tatsächlich funktioniert, danke. – larsrh

Verwandte Themen