0

JavaFX で書かれた GUI である 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 の指示に従って、JavaFX ランタイムを classpath に配置しました。OSGi コンテナーとして apache felix を使用しています。これらすべてが一緒になってかなりうまく機能します。(IE: JavaFX GUI を OSGi バンドルとして作成でき、動作します!)

私が抱えている問題は、JavaFX の MigLayout ライブラリにあります。私はこれらの依存関係を使用しています:

<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 Peter Kriens の bnd ツールのコマンドを使用して MigLayout jar ファイルを変更し、マニフェスト ファイルに必要な 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-core-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 コンテナーに入れても問題は解決しません。同じエラーが発生します。

OSGi コンテナー内で MigLayout ライブラリーを使用できるように、他に何を試せばよいかわかりません。

OSGi コンテナー内で MigLayout を使用するには、どうすればよいですか?

編集

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

また、クライアント用に定義された Import-Packages もここにあります。ビルドは、実際には scala および osgi プラグインを使用して gradle で行われます。

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'

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

編集2

クラスローダーを設定するというトムソントムの提案に従って、fxmlをロードするコードを変更しました。更新されたコードは次のとおりです。

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

まだ ClassNotFoundException を生成しています。しかし今回は、スタック トレースには、トレースがどのバンドルからのものかに関する情報は含まれていません。微妙な違いで、意味がわかりません。何か案は?

編集3

bnd コマンド ライン ユーティリティを使用した後、両方の MigLayout 依存関係に対して生成された MANIFEST.MF ファイルを含めるように OP を変更しました。

編集4

client.jar MANIFEST.MF (私のパッケージは単なるクライアントではなく、実際には xxclient であることに注意してください。ただし、SO のパッケージ名から xx を削除しました。これが、この 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)"

編集5

問題を確認するためにダウンロードできるプロジェクトを GitHubに作成しました。現在チェックインされているこのプロジェクトは、ファイル README.txt の指示に従えば、チェックアウト後すぐに実行されます。

MigLayout ライブラリで発生している問題を確認したい場合は、MigPaneclient/src/main/resources/fxml/login.fxmlのインポートのコメントを外してファイルを編集してください。プロジェクトを再構築し、新しく作成された client-1.0.0-SNAPSHOT.jar をapp/bundle/ディレクトリにコピーし、コマンドで felix-cache ディレクトリをクリアしてから、コマンド$ rm -rf app/felix-cache/でディレクトリ内から felix ランチャーを再起動します。app$ java -jar bin/felix.jar

4

2 に答える 2

1

fxml の問題は、正しいクラスローダーを設定していないことだと 99% 確信しています。fxml 読み込みコードを表示してください

于 2013-11-09T19:38:42.103 に答える
0

UI バンドルには、対応する Import-Package ステートメントも必要です。これらを作成する最も簡単な方法は、UI バンドルの pom でmaven バンドル プラグインを使用することです。

バンドル プラグインを使用する場合は、結果のマニフェストをチェックして、必要なすべての Import-Package ステートメントが存在することを確認します。

于 2013-11-09T19:29:22.633 に答える