2015-04-11 5 views
9

Ho un build SBT multi-modulo composto da api, core e third-party. La struttura è più o meno questo:Utilizzo di un caricatore di classe personalizzato per una dipendenza del modulo in SBT

api 
|- core 
|- third-party 

Il codice per third-party implementa api e viene copiato pari pari da qualche altra parte, in modo da non voglio davvero toccarlo.

A causa del modo in cui è implementato third-party (uso intensivo di singleton), non posso semplicemente fare in modo che core dipenda da third-party. In particolare, ho solo bisogno di usarlo tramite il api, ma ho bisogno di avere più copie isolate di third-party in fase di esecuzione. (. Questo mi permette di avere più "single", allo stesso tempo)

Se sto correndo al di fuori della mia SBT costruire, io faccio solo questo:

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

Ma il problema è che io don 'so come capire a runtime cosa dovrei dare come argomento a URLClassLoader se sto correndo via sbt core/run.

+1

1. Come intendete raggruppare il progetto? SBT-montaggio? O il third-party.jar sarà esterno? 2. Quale forma sono i singleton di terze parti? oggetto? Chiedo perché hai menzionato .newInstance() –

+0

@DaleWijnand 1. Non ho ancora deciso, ma è probabile che lascerò "sbt-assembly' bundle' api', 'core' e la libreria di Scala e' third-party .jar' separatamente. – larsrh

+0

@DaleWijnand 2. È piuttosto complicato. Fondamentalmente, la libreria di terze parti gestisce alcuni processi esterni. Può gestire molti di questi processi contemporaneamente, ma deve essere inizializzato. Al momento dell'inizializzazione, popola una mappa all'interno di un oggetto (ad esempio un oggetto 'Scala 'letterale), ad es. il percorso verso il processo esterno. Il mio codice deve essere in grado di gestire più posizioni di quel processo esterno. – larsrh

risposta

4

Questo dovrebbe funzionare, anche se non l'ho provato con la configurazione.

L'idea di base è di consentire a sbt di scrivere il classpath in un file che è possibile utilizzare in fase di runtime. sbt-buildinfo fornisce già una buona base per questo, quindi lo userò qui, ma tu potresti estrarre solo la parte relativa e non usare anche questo plugin.

Aggiungi questo alla tua definizione del progetto:

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

In fase di esecuzione, utilizzare questo:

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 contiene solo le classi compilate per il progetto (ad esempio .../target/scala-2.10/classes/). A seconda della configurazione, è possibile utilizzare fullClasspath anziché (che contiene anche la libreriaDipendenze e progetti dipendenti) o qualsiasi altra chiave relativa al percorso di classe.

+0

Sembra fantastico, ci proverò. – larsrh

+0

Ci scusiamo per la risposta tardiva. La tua soluzione ha funzionato davvero, grazie. – larsrh