XULRunner with Java: JavaXPCOM Tutorial 1

本文介绍如何使用JavaXPCOM在Java程序中集成XULRunner,包括初始化JavaXPCOM、使用XPCOM组件及服务等关键技术。适用于希望了解XPCOM架构及在Java环境中使用XULRunner的开发者。

这篇教程更新于2008年夏天,用来集成SWT和XULRunner来在SWT里显示一个浏览器。要想获得更多信息,请参考
http://www.eclipse.org/swt/faq.php#whatisbrowser

1,简介
       这篇教程有两个目的。首先,我们能学到一些XPCOM的概念和怎么通过JavaXPCOM来在java里使用它。其次,我们学到怎么在

java程序里嵌入Firefox浏览器。
       这里的例子我们开发,编译和测试的环境是Ubuntu 8.04和JDK1.6(译注,我使用的是windows xp)。我们在windows下遇到

一个问题,用户无法在form里输入文本,虽然在例子里并不需要这点。
1.1 下载资源  
      例子的源码在相关章节的开始以及文章的最后都有下载,当然,你也可以下载所有的代码
http://ladyr.es/wiki/attachment/wiki/XPCOMGuide/complete-guide-resources-windows.zip  windows
http://ladyr.es/wiki/attachment/wiki/XPCOMGuide/complete-guide-resources-linux.zip   linux

2. 安装XULRunner
     JavaXPCOM是一个用来访问XULRunner里XPCOM组件和服务的java库。在这里我们介绍了一些方法通过java来使用XULRunner组件

和服务。JavaXPCOM也能在java程序里嵌入Gecko。JavaXPCOM需要Java1.4.2或者更新的版本。为了使用JavaXPCOM你需要下面的步骤


    XULRunner:你需要安装XULRunner。它在开发中并不断变化。这个教程使用的是1.9的版本,这个版本built的时间是2008-6-19.

安装XULRunner的方法:
      1. 根据自己的操作系统下载合适的安装包(译注,我在windows xp下无法使用XULRunner1.9,我使用的是
                xulrunner-1.8.1.3.en-US.win32-20080128)
   2.解压它,比如把它解压到c:/XULRunner
   3.进入这个路径
   4.卸载以前的注册
   5.注册这个版本
   (译注:注册并不是必须的,注册后可以在程序里不用知道XULRunner的路径,不注册也可以,不过程序里要知道路径)
javaxpcom.jar
MozillaInterface.jar
MozillaGlue.jar

3.在Java里使用JavaXPCOM
3.1 JavaXPCOM初始化
XPCOM(Mozilla Cross-platform Object Model)是一个机遇组件的架构。为了使用XPCOM,我们需要一些初始化和清理的代码。有

两种方法来使用它,请看下面注释过的代码:
首先我们可以使用initXPCOM方法


为了嵌入,你应该使用 initEmbedding 方法。这个方法需要实现 IAppFileLocProvider 的类。我们使用包




关于更多ATF的信息,请访问http://www.eclipse.org/atf/
 
为了嵌入而初始JavaXPCOM的代码如下:



3.2 XPCOM组件和接口的基本概念
   XPCOM(Mozilla Cross-platform Object Model)如它名字,是一个基于组件的架构。XPCOM的组件一般是本地实现的。XPCOM系统

里的所有组件都有一个唯一的标识。XPCOM组件实现许多接口,这些接口提供各种功能。同一个组件可能实现不同的接口,同样,一

个接口可能北不同的组件实现。因为接口必需有一个具体的实现才能使用,我们将讨论组件和接口。在后面,我们把组件看成一段实

现了一个或多个接口的代码。
   在XULRunner里,接口名总是以nsI开头。在JavaXPCOM里,每个XPCOM接口都有一个对应的java接口。它们的名字以nsI开头,它们

位于org.mozilla.xpcom里,被放在MozillaInterfaces.jar包里面。这些接口的javaDoc在MozillaInterfaces-src.jar里。你可以在

https://developer.mozilla.org/En浏览所有的XPCOM接口,MDC是一个与语言无关的文档,并不是Java的。
   在JavaXPCOM里没有XPCOM组件的代码。后面你将看到怎么在JavaXPCOM里初始化和使用组件。因为没有java代码,所有也没有

JavaDoc。http://developer.mozilla.org/En里包含了XPCOM组件的完整文档。
   我们可以如下的步骤使用XPCOM组件:
      得到组件
      得到实现了我们想使用的接口的组件
      调用它
   组件使用URI来命名,例如,@mozilla.org/file/local;1。Mozilla的组件以@mozilla.org开头。这个URI是contractID,每个组

件也有一个组件标识符(CID)。同样,每一个接口也有一个接口标识符(IID)。IID总能使用一个static的final的属性来获取。组件可

能存在一个对象实例或者单例的服务。通常,服务包含单词Service或者Manager

3.3 使用JavaXPCOM: 与用JavaScript来使用XPCOM的不同点与相同点
   在XPCOM里,所有的接口都继承了nsISupports,它有一个函数叫做QueryInterface,这个函数返回一个组件的一个接口,如果这

个组件没有实现这个接口,那么将返回异常。在JavaXPCOM里,接口nsISupports提供了QueryInterface来返回一个nsISupports对象
。为了属于谋个接口的方法,你首先需要使用QueryInterface方法确定这个接口。

   比如,你可以使用下面的JavaScript来调用XPCOM来创建一个本地文件组件:
// Get the component
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();

// Get the part of the component corresponding to the interface that we want to use
var aLocalFile = aFile.QueryInterface(Components.interfaces.nsILocalFile);

// Call the function (one or more than one)
aLocalFile.initWithPath("/mozilla/testfile.txt");


上面需要两行JavaScript代码来创建组件和获得接口。可以简化的代码:
// Get the component and directly the part of the component corresponding to the interface that we want to use
var aLocalFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);

// Call the function (one or more than one)
aLocalFile.initWithPath("/mozilla/testfile.txt");

在JavaXPCOM里,为了得到XPCOM组件相对应的JavaXPCOM的实例,我们必须访问ComponentManager对象,这个对象允许在运行时访问

和使用。它能够通过单例类Mozilla的getComponentManager()访问。每次我们用这种方法实例化,我们创建了一个XPCOM组件。有两

种方法来创建XPCOM组件的实例:
   使用组件ID(CID):
// Get the component manager
ComponentManager cm = Mozilla.getInstance().getComponentManager();

// Get the part of the component corresponding to the interface that we want to use
nsISupports obj = cm.createInstance("{6ddb050c-0d04-11d4-986e-00c04fa0cf4a}", null, nsISupports.NS_ISUPPORTS_IID);

// Get the part of the component corresponding to the interface that we want to use
nsIInputStreamChannel isc = obj.queryInterface(nsIInputStreamChannel.NS_IINPUTSTREAMCHANNEL_IID);

// Call the method (one or more than one)
System.out.println("Content-Length: " + isc.getContentLength());

我们可以简化一下:
// Get the component manager
ComponentManager cm = Mozilla.getInstance().getComponentManager();

// Get the part of the component corresponding to the interface that we want to use
nsIInputStreamChannel isc = (nsIInputStreamChannel)
         cm.createInstance("{6ddb050c-0d04-11d4-986e-00c04fa0cf4a}", null,

nsIInputStreamChannel.NS_IINPUTSTREAMCHANNEL_IID);

// Call the method (one or more than one)
System.out.println("Content-Length: " + isc.getContentLength());

也可以通过contractID来访问:
// Get the component manager
ComponentManager cm = Mozilla.getInstance().getComponentManager();

// Get the part of the component corresponding to the interface that we want to use
nsISupports obj = cm.createInstanceByContractID("@ mozilla.org/file/local;1", null, nsISupports.NS_ISUPPORTS_IID);

// Get the part of the component corresponding to the interface that we want to use
nsILocalFile file = obj.queryInterface(nsILocalFile.NS_ILOCALFILE_IID);

// Call the method (one or more than one)
file.initWithPath("/home/alpgarcia/xpcom-file-test.txt");

简化形式:
// Get the component manager
ComponentManager cm = Mozilla.getInstance().getComponentManager();

// Get the part of the component corresponding to the interface that we want to use
nsILocalFile file = (nsILocalFile)cm.createInstanceByContractID("@ mozilla.org/file/local;1", null,

nsILocalFile.NS_ILOCALFILE_IID);

// Call the method (one or more than one)
file.initWithPath("/home/alpgarcia/xpcom-file-test.txt");

在JavaScript里,我们通过调用instanceOf来检查一个组件是否实现了一个接口:

var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();
if (aFile instanceOf Components.interfaces.nsILocalFile){
   var aLocalFile = aFile.QueryInterface(Components.interfaces.nsILocalFile);
   ...
}


在java里,instanceof运算符是其它的含义。在java里只能通过QueryInterface来判断。如果调用失败,那么这个组件就没有实现特

定的接口。如果我们还是用JavaScript的写法,那么就会得到不同的结果(错误的)。试试下面的代码:



JavaScript和Java的主要区别是,在JavaXPCOM里没有一个特定的对象与XPCOM组件对应。
另外,我们可以使用另一类组件,叫做services。前面我们说过,Service作为一个单例存在,换句话说,你只能初始化一次。

Services被用来get和set全局的数据或者对其它对象进行操作。创建Service与创建组件类似,把getInstance()改成getService()就

行了。在JavaXPCOM里,我们需要nsIServiceManager对象来初始化一个Service。然后你需要调用getService或者

getServiceByContractID来得到service组件的引用。

在JavaScript里的代码:
// get the dns service component in JavaScript
var dnsservice = Components.classes["@mozilla.org/network/dns-service;1"].getService();
dnsservice.QueryInterface(Components.interfaces.nsIDNSService);

JavaXPCOM的代码:
//Now, we will try to use a service, for example, dns service
nsIServiceManager sm = mozilla.getServiceManager();
                               
nsIDNSService dns = (nsIDNSService) sm.getServiceByContractID(
        "@mozilla.org/network/dns-service;1", nsIDNSService.NS_IDNSSERVICE_IID);


3.4 在JavaXPCOM里使用组件和Services的完整例子
在第一个例子里我们将要创建一个本地的文件并写入一些文本。
package es.ladyr.javaxpcom.test;




下面是一个关于域名的例子:

!SESSION 2025-05-26 20:01:51.887 ----------------------------------------------- eclipse.buildId=4.29.0.20230907-1200 java.version=17.0.8.1 java.vendor=Eclipse Adoptium BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=zh_CN Framework arguments: -product org.eclipse.epp.package.java.product Command-line arguments: -os linux -ws gtk -arch x86_64 -product org.eclipse.epp.package.java.product !ENTRY ch.qos.logback.classic 1 0 2025-05-26 20:02:04.760 !MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. !ENTRY org.eclipse.osgi 4 0 2025-05-26 20:02:07.219 !MESSAGE Application error !STACK 1 java.lang.UnsatisfiedLinkError: Could not load SWT library. Reasons: no swt-pi4-gtk-4962r3 in java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib no swt-pi4-gtk in java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib no swt-pi4 in java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib Can&#39;t load library: /root/.swt/lib/linux/x86_64/libswt-pi4-gtk-4962r3.so Can&#39;t load library: /root/.swt/lib/linux/x86_64/libswt-pi4-gtk.so Can&#39;t load library: /root/.swt/lib/linux/x86_64/libswt-pi4.so at org.eclipse.swt.internal.Library.loadLibrary(Library.java:346) at org.eclipse.swt.internal.Library.loadLibrary(Library.java:255) at org.eclipse.swt.internal.gtk.OS.<clinit>(OS.java:97) at org.eclipse.swt.internal.Converter.wcsToMbcs(Converter.java:209) at org.eclipse.swt.internal.Converter.wcsToMbcs(Converter.java:155) at org.eclipse.swt.widgets.Display.<clinit>(Display.java:169) at org.eclipse.ui.internal.Workbench.createDisplay(Workbench.java:727) at org.eclipse.ui.PlatformUI.createDisplay(PlatformUI.java:183) at org.eclipse.ui.internal.ide.application.IDEApplication.createDisplay(IDEApplication.java:182) at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:125) at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:203) at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:136) at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:402) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:255) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:651) at org.eclipse.equinox.launcher.Main.basicRun(Main.java:588) at org.eclipse.equinox.launcher.Main.run(Main.java:1459) at org.eclipse.equinox.launcher.Main.main(Main.java:1432)
最新发布
05-27
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值