Я пытаюсь создать пакет OSGi, который является графическим интерфейсом, написанным на JavaFX. Моя установка выглядит следующим образом:Как конвертировать MigLayout для JavaFX в пакет OSGi, чтобы он работал внутри контейнера OSGi?
OS name: "linux", version: "3.8.0-25-generic", arch: "amd64", family: "unix"
Java version: 1.7.0_45, vendor: Oracle Corporation
Apache Maven 3.1.1 (0728685237757ffbf44136acec0402957f723d9a; 2013-09-17 10:22:22-0500)
Я следовал инструкциям по JavaFX Maven Plugin to put the JavaFX runtime on the classpath. Я использую apache felix в качестве контейнера OSGi. Все это вместе работает довольно хорошо. (IE: Я могу создать графический интерфейс JavaFX в качестве пакета OSGi, и он работает!)
Проблема, которая у меня есть с библиотекой MigLayout для JavaFX. Я использую эти зависимости:
<dependency>
<groupId>com.miglayout</groupId>
<artifactId>miglayout-core</artifactId>
<version>4.2</version>
</dependency>
<dependency>
<groupId>com.miglayout</groupId>
<artifactId>miglayout-javafx</artifactId>
<version>4.2</version>
</dependency>
Ни один из них являются OSGi расслоения, и зависимость от них вызывает ошибку во время выполнения происходит в приложении, а именно:
javafx.fxml.LoadException: java.lang.ClassNotFoundException: org.tbee.javafx.scene.layout.fxml.MigPane from bundle 7 (client)
at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2489)
at javafx.fxml.FXMLLoader.processImport(FXMLLoader.java:2333)
at javafx.fxml.FXMLLoader.processProcessingInstruction(FXMLLoader.java:2301)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2154)
at client.Gui.start(Gui.scala:22)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:216)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:76)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:89)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.ClassNotFoundException: org.tbee.javafx.scene.layout.fxml.MigPane from bundle 7 (client)
at akka.osgi.impl.BundleDelegatingClassLoader.loadClass(BundleDelegatingClassLoader.scala:49)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at javafx.fxml.FXMLLoader.loadTypeForPackage(FXMLLoader.java:2557)
at javafx.fxml.FXMLLoader.loadType(FXMLLoader.java:2546)
at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2487)
... 14 more
Caused by: java.lang.ClassNotFoundException: org.tbee.javafx.scene.layout.fxml.MigPane not found by com.typesafe.akka.osgi [1]
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1532)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:75)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1955)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at akka.osgi.impl.BundleDelegatingClassLoader.loadClass(BundleDelegatingClassLoader.scala:46)
... 18 more
Вот пример FXML файл, который я пытаюсь загрузить, login.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Button?>
<?import org.tbee.javafx.scene.layout.fxml.MigPane?>
<StackPane xmlns:fx="http://javafx.com/fxml" fx:controller="client.view.LoginView" fx:id="pane">
<children>
<Button text="Login" fx:id="loginButton" onAction="#login"/>
</children>
</StackPane>
Пожалуйста, обратите внимание, что если я запускаю это приложение за пределами контейнера OSGi, страница загружается нормально. Но внутри контейнера OSGi он терпит неудачу с вышеупомянутой трассировкой стека. Кроме того, если я прокомментирую импорт для MigPane, он также загрузится в контейнер OSGi. Таким образом, пример ниже отлично работает внутри контейнера OSGi:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Button?>
<!--<?import org.tbee.javafx.scene.layout.fxml.MigPane?>-->
<StackPane xmlns:fx="http://javafx.com/fxml" fx:controller="client.view.LoginView" fx:id="pane">
<children>
<Button text="Login" fx:id="loginButton" onAction="#login"/>
</children>
</StackPane>
Я попытался решить эту проблему с помощью wrap
command on Peter Kriens' bnd tool изменять банку файлы MigLayout таким образом они будут включать в себя необходимые OSGi метаданных в файле манифеста.
Команды:
// The original jar (the one specified in the aforementioned maven
// dependency - which is not an OSGi bundle) is contained in the
// wrap subdirectory.
$ java -jar bnd-2.1.0.jar wrap miglayout-core-4.2.jar wrap/miglayout-core-4.2.jar
$ java -jar bnd-2.1.0.jar wrap miglayout-javafx-4.2.jar wrap/miglayout-javafx-4.2.jar
MigLayout-ядро-4.2.jar MANIFEST.MF:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Bnd-LastModified: 1384015623755
Build-Jdk: 1.6.0_29
Built-By: Mike
Bundle-ManifestVersion: 2
Bundle-Name: miglayout.core
Bundle-SymbolicName: miglayout.core
Bundle-Version: 0
Created-By: 1.7.0_45 (Oracle Corporation)
Export-Package: net.miginfocom.layout
Originally-Created-By: Apache Maven
Tool: Bnd-2.1.0.20130426-122245
MigLayout-JavaFX-4.2.jar MANIFEST.MF:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Bnd-LastModified: 1384015600603
Build-Jdk: 1.6.0_29
Built-By: Mike
Bundle-ManifestVersion: 2
Bundle-Name: miglayout.javafx
Bundle-SymbolicName: miglayout.javafx
Bundle-Version: 0
Created-By: 1.7.0_45 (Oracle Corporation)
Export-Package: org.tbee.javafx.scene.layout;uses:="javafx.scene,javafx.
scene.layout,net.miginfocom.layout",org.tbee.javafx.scene.layout.fxml;u
ses:="javafx.beans,javafx.scene,net.miginfocom.layout,org.tbee.javafx.s
cene.layout"
Import-Package: javafx.beans;resolution:=optional,javafx.collections;res
olution:=optional,javafx.geometry;resolution:=optional,javafx.scene;res
olution:=optional,javafx.scene.control;resolution:=optional,javafx.scen
e.layout;resolution:=optional,javafx.scene.paint;resolution:=optional,j
avafx.scene.shape;resolution:=optional,javafx.stage;resolution:=optiona
l,net.miginfocom.layout;resolution:=optional
Originally-Created-By: Apache Maven
Tool: Bnd-2.1.0.20130426-122245
Но размещение результирующего «пакета» в контейнере OSGi не устраняет проблему. Я получаю ту же ошибку.
Я не знаю, что еще попробовать, поэтому я могу использовать библиотеку MigLayout внутри контейнера OSGi.
Что мне нужно для этого, я могу использовать MigLayout внутри контейнера OSGi?
EDIT
Вот код, который загружает FXML. Это написано в Scala.
package client
import _root_.javafx.application.Application
import _root_.javafx.stage.Stage
import _root_.javafx.scene.{Parent, Scene}
import _root_.javafx.fxml.FXMLLoader
import client.view.{Login, Screen}
class Gui extends Application {
def start(stage: Stage) {
val initialScreen: Screen = Login
stage.setTitle("GUI")
val loader = new FXMLLoader
loader.setLocation(initialScreen.url)
try {
val root: Parent = loader.load(initialScreen.inputStream).asInstanceOf[Parent]
val scene: Scene = new Scene(root, 800, 600)
scene.getStylesheets.add("/fxml/styles/styles.css")
stage.setScene(scene)
stage.show()
} catch {
case t: Throwable => t.printStackTrace()
}
}
override def stop() {
System.out.println("Stopping JavaFX Application")
Container.shutdown()
}
}
Кроме того, здесь указаны Импорт-пакеты, определенные для клиента. Сборка на самом деле выполняется с помощью градиента с использованием плагинов scala и osgi.
фрагмент сборки.Gradle:
def importPackages =
' akka.actor' +
', akka.actor.dungeon' +
', akka.event' +
', akka.osgi' +
', javafx.application' +
', javafx.beans' +
', javafx.collections' +
', javafx.fxml' +
', javafx.geometry' +
', javafx.scene' +
', javafx.scene.control' +
', javafx.scene.image' +
', javafx.scene.layout' +
', javafx.scene.paint' +
', javafx.scene.shape' +
', javafx.stage' +
', net.miginfocom.layout' +
', org.osgi.framework' +
', org.tbee.javafx.scene.layout' +
', org.tbee.javafx.scene.layout.fxml' +
', scala' +
', scala.collection' +
', scala.reflect' +
', scala.runtime'
jar {
manifest {
name = "client (OSGi bundle)"
instruction 'Bundle-Vendor', 'Company'
instruction 'Bundle-Description', 'Client (OSGi bundle)'
instruction 'Private-Package', 'client'
instruction 'Bundle-Activator', 'client.ClientActivator'
instruction 'Import-Package', importPackages
}
}
EDIT 2
Я изменил код, который загружает FXML в соответствии с предложением tomsontom, чтобы установить загрузчик класса. Вот обновленный код:
фрагмент из build.gradle:
def importPackages =
' akka.actor' +
', akka.actor.dungeon' +
', akka.event' +
', akka.osgi' +
', javafx.application' +
', javafx.beans' +
', javafx.collections' +
', javafx.fxml' +
', javafx.geometry' +
', javafx.scene' +
', javafx.scene.control' +
', javafx.scene.image' +
', javafx.scene.layout' +
', javafx.scene.paint' +
', javafx.scene.shape' +
', javafx.stage' +
', net.miginfocom.layout' +
', org.osgi.framework' +
', org.tbee.javafx.scene.layout' +
', org.tbee.javafx.scene.layout.fxml' +
', scala' +
', scala.collection' +
', scala.reflect' +
', scala.runtime'
FXML загрузки Код:
def start(stage: Stage) {
val initialScreen: Screen = Login
stage.setTitle("GUI")
val loader = new FXMLLoader
loader.setClassLoader(getClass.getClassLoader)
loader.setLocation(initialScreen.url)
try {
val root: Parent = loader.load(initialScreen.inputStream).asInstanceOf[Parent]
val scene: Scene = new Scene(root, 800, 600)
scene.getStylesheets.add("/fxml/styles/styles.css")
stage.setScene(scene)
stage.show()
} catch {
case t: Throwable => t.printStackTrace()
}
}
Но обратите внимание, что теперь я получаю подобное, но другое сообщение об ошибке при запуске приложения в контейнере OSGi:
javafx.fxml.LoadException: java.lang.ClassNotFoundException: org.tbee.javafx.scene.layout.fxml.MigPane
at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2489)
at javafx.fxml.FXMLLoader.processImport(FXMLLoader.java:2333)
at javafx.fxml.FXMLLoader.processProcessingInstruction(FXMLLoader.java:2301)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2154)
at client.Gui.start(Gui.scala:23)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:216)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:76)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:89)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.ClassNotFoundException: org.tbee.javafx.scene.layout.fxml.MigPane
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at org.apache.felix.framework.ExtensionManager$ExtensionManagerWiring.getClassByDelegation(ExtensionManager.java:873)
at org.apache.felix.framework.BundleWiringImpl.searchImports(BundleWiringImpl.java:1553)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1484)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:75)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1955)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at javafx.fxml.FXMLLoader.loadTypeForPackage(FXMLLoader.java:2557)
at javafx.fxml.FXMLLoader.loadType(FXMLLoader.java:2546)
at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2487)
... 14 more
Он по-прежнему генерирует класс ClassNotFoundExcep Тион. Но на этот раз трассировка стека не содержит ничего, относящегося к тому, из какого пакета происходит трассировка. Это тонкая разница, и я не знаю, что это значит. Есть идеи?
РЕДАКТИРОВАТЬ 3
Я изменил OP, чтобы включать в себя сгенерированный файл MANIFEST.MF для обоих зависимостей MigLayout после использования Bnd утилиты командной строки.
EDIT 4
client.jar MANIFEST.MF (Обратите внимание, мой пакет действительно xxclient, а не просто клиентом. Но я раздели хх от имени пакета для SO. То есть причина для межстрочного интервала расхождения в этом MANIFEST.MF файле):
Manifest-Version: 1.0
Export-Package: client;version="1.0.0.SNAPSHOT";uses:=
"akka.actor,akka.osgi,javafx.application,javafx.stage,org.osgi.framew
ork,scala.reflect",client.controller;version="1.0.0.S
NAPSHOT";uses:="akka.actor,akka.event,scala,scala.reflect,scala.runti
me",client.message;version="1.0.0.SNAPSHOT";uses:="sc
ala,scala.collection,scala.reflect",client.model;vers
ion="1.0.0.SNAPSHOT";uses:="scala.reflect",client.vie
w;version="1.0.0.SNAPSHOT";uses:="scala,scala.collection,scala.reflec
t"
Private-Package: client
Tool: Bnd-2.1.0.20130426-122213
Bundle-Name: client (OSGi bundle)
Created-By: 1.7.0_45 (Oracle Corporation)
Bundle-Vendor: Company
Bundle-Version: 1.0.0.SNAPSHOT
Bnd-LastModified: 1384035615000
Bundle-ManifestVersion: 2
Bundle-Activator: client.ClientActivator
Bundle-Description: Client (OSGi bundle)
Bundle-SymbolicName: client
Import-Package: akka.actor;version="[2.2,3)",akka.event;version="[2.2,
3)",akka.osgi;version="[2.2,3)",javafx.application,javafx.collections
,javafx.fxml,javafx.scene,javafx.stage,org.osgi.framework;version="[1
.7,2)",scala;version="[2.10,3)",scala.collection;version="[2.10,3)",s
cala.reflect;version="[2.10,3)",scala.runtime;version="[2.10,3)",net.
miginfocom.layout,org.tbee.javafx.scene.layout.fxml,org.tbee.javafx.s
cene.layout,javafx.scene.layout,javafx.scene.image,javafx.scene.contr
ol,javafx.scene.shape,javafx.scene.paint,javafx.geometry,javafx.beans
,akka.actor.dungeon;version="[2.2,3)"
EDIT 5
Я создал project on GitHub, который вы можете скачать, чтобы увидеть проблему. Как и в данный момент, этот проект должен запускаться сразу после проверки, если вы следуете инструкциям в файле README.txt.
Если вы хотите увидеть проблему, с которой я столкнулся с библиотекой MigLayout, отредактируйте файл client/src/main/resources/fxml/login.fxml
, раскомментировав импорт для MigPane. Переконструируйте проект, скопируйте вновь созданный клиент-1.0.0-SNAPSHOT.jar в каталог app/bundle/
, очистите каталог felix-cache с помощью $ rm -rf app/felix-cache/
, а затем перезапустите пусковую установку felix из каталога app
с помощью команды $ java -jar bin/felix.jar
.
Да, я должен был включить это в OP. У меня есть операторы импорта в наборе клиентов. Я смущен, почему он не работает. – axiopisty
Единственная другая идея заключается в том, что, возможно, в ядре midlayout нет оператора Import-Package, и исключение возникает в основном классе miglayout, который, конечно же, использует свой собственный загрузчик классов. –
Разве это не приведет к появлению другого сообщения об ошибке, что-то о том, что невозможно связать связки вместе? – axiopisty