本教程将指导你创建一个XWiki组件,这是一种方式来扩展或定制XWiki platform。事实上,XWiki platform是由组件组成,可以通过你的实现来替换默认的实现。也可以添加新的组件来扩展platform,例如通过实现新的渲染宏。

组件目前已经替换旧的插件。

你应该先阅读XWiki组件的参考文档。

让我们开始吧!

在下面的教程中,我们将引导你写一个简单的组件,帮助你快速学习XWiki组件,并解释它是如何工作的。

使用Maven创建一个XWiki组件

正如你阅读XWiki组件参考文档,编写一个组件是由三个步骤构成(组件接口,组件实现,注册组件)。

为了让你更容易上手,我们已经创建了一个Maven原型来帮助创建一个简单的组件模块。

在你安装Maven之后,打开一个命令提示符并键入:mvn archetype:generate。

这将列出Maven Central所有可用的原型。相反,如果你想直接使用XWiki组件原型,可以直接输入(版本改为你想要使用的版本):

mvn archetype:generate \
  -DarchetypeArtifactId=xwiki-commons-component-archetype \
  -DarchetypeGroupId=org.xwiki.commons \
  -DarchetypeVersion=5.4.4

然后按照说明进行操作。例如:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] Archetype repository missing. Using the one from [org.xwiki.commons:xwiki-commons-component-archetype:6.1-milestone-1] found in catalog remote
Define value for property 'groupId': : com.acme
Define value for property 'artifactId': : example
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  com.acme: :
Confirm properties configuration:
groupId: com.acme
artifactId: example
version: 1.0-SNAPSHOT
package: com.acme
 Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: xwiki-commons-component-archetype:5.4.4
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.acme
[INFO] Parameter: artifactId, Value: example
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.acme
[INFO] Parameter: packageInPathFormat, Value: com/acme
[INFO] Parameter: package, Value: com.acme
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.acme
[INFO] Parameter: artifactId, Value: example
[INFO] project created from Archetype in dir: /private/tmp/example
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 25.019s
[INFO] Finished at: Thu May 29 18:49:46 CEST 2014
[INFO] Final Memory: 11M/26M
[INFO] ------------------------------------------------------------------------

然后到创建的目录(在上面的例子中的example)下,运行mvn install编译你的组件。

Component说明

假设,下面的说明,你所使用的包是com.acme

在组件项目下浏览文件夹,你会看到下面标准的Maven项目结构:

pom.xml
src/main/java/com/acme/HelloWorld.java
src/main/java/com/acme/internal/DefaultHelloWorld.java
src/main/java/com/acme/internal/HelloWorldScriptService.java
src/main/resources/META-INF/components.txt
src/test/java/com/acme/HelloWorldTest.java

默认创建的文件:HelloWorld为接口(又名component role),它的实现DefaultHelloWorld (component implementation),一个对应组件的测试类HelloWorldTest,组件定义文件components.txt,Maven项目pom.xml文件。HelloWorldScriptService文件用于解释在wiki页面让如何调用组件API。

如果你看下pom.xml文件你会注意到以下依赖:

<dependencies>
   <dependency>
     <groupId>org.xwiki.commons</groupId>
     <artifactId>xwiki-commons-component-api</artifactId>
     <version>${commons.version}</version>
   </dependency>
   <!-- Testing dependencies -->
   <dependency>
     <groupId>org.xwiki.commons</groupId>
     <artifactId>xwiki-commons-test</artifactId>
     <version>${commons.version}</version>
     <scope>test</scope>
   </dependency>
 </dependencies>

上面的代码定义了xwiki-core-component-api的依赖,这就是XWiki组件概念的定义。还依赖了xwiki-core-shared-tests,提供辅助类来轻松测试组件。

接口文件(HelloWorld.java)包含一个普通的Java接口的定义,如下所示:

@Role /* annotation used for declaring the service our component provides */
public interface HelloWorld
{
    String sayHello();
}

请记住这个接口指定了其他组件可以在你的组件上使用的API。在我们的例子中,我们将构建一个组件调用sayHello()。

然后我们实现接口,DefaultHelloWorld类。

@Component /* annotation used for declaring a component implementation */
@Singleton /* annotation used for defining the component as a singleton */
public class DefaultHelloWorld implements HelloWorld

请注意,有一个@Named注解来指定一个组件hint。这是有用的,尤其是当我们要区分相同类型组件的若干实现。假想一下,我们有一个特殊的Hello World实现从数据库获取问候信息:

@Component
@Named("database")
public class DatabaseHelloWorld implements HelloWorld

然后DefaultHelloWorld的sayHello方法(为了简单起见,直接返回Hello world!):

/**
 * Says hello by returning a greeting to the caller.
 *
 * @return A greeting.
 */
public String sayHello()
{
  return "Hello world!";
}

在components.txt文件注册。

com.acme.internal.DefaultHelloWorld

如何找到我的组件并且使用它?

从其他组件

为了从其他组件访问你的组件,我们使用components engine,指定依赖关系,通过组件管理器(component manager)来处理组件实例化和注入。

为了使用HelloWorld组件,你需要一个组件的引用来使用它。为此,你需要使用一个成员变量,例如,一个Socializer组件中引用:

@Component
@Singleton
public class DefaultSocializer implements Socializer
{
    [...]

/** Will be injected by the component manager */
    @Inject
    private HelloWorld helloWorld;

/** Will be injected by the component manager */
    @Inject
    @Named("database")
    private HelloWorld databaseWorld;

[...]
}

注意@Inject注解,指示组件管理器在需要的时候注入所需的组件。

这样,你现在就可以在DefaultSocializer类使用helloWorld成员变量,当使用Socializer类时,它由组件管理器分配,在运行时提供HelloWorld组件。如:

public class DefaultSocializer implements Socializer
{
    [...]

public void startConversation()
    {
        this.helloWorld.sayHello();
       
        [...]
    }

[...]
}

注意,所有两个组件之间的调用过程中,我们从来没有看到组件实现,所有是通过rolesinterfaces完成:一个服务的实现完全隐藏于外部组件的任何代码。

非组件Java代码(例如旧的插件)

对于这种用法,因为我们不能使用基于组件的架构优势和组件管理器(component manager)的“神奇”,在XWiki团队已经创建了一个辅助方法就像在组件代码和非组件代码之间的桥梁,com.xpn.xwiki.web.Utils.getComponent(String role, String hint)从组件管理器获取指定组件实例,并返回它。如在前面的章节中看到的,hint是一个可选标识符,附加role,用于区分相同的接口的实现:roles识别服务,而hints有助于实现区分实现。getComponent函数也有一个没有带hint参数,使用的是默认的hint。

要使用我们上面提供的组件,如下调用:

HelloWorld greeter = Utils.getComponent(HelloWorld.class);
greeter.sayHello();

HelloWorld databaseGreeter = Utils.getComponent(HelloWorld.class, "database");
greeter.sayHello();

即使该函数返回的对象是DefaultHelloWorld一个实例,你也永远不应该声明你实现类型的对象,也不应该转换为实现。

一个组件是由它的接口表示,对于这样一个服务的实现可以通过任何代码、任何类提供,所以依靠实现类型既不是很好的做法,也是不安全的。在未来,一个Maven enforcer插件将在构建生命周期的设置,因此,任何引用的组件实现(位于“internal”子包)会导致编译错误。

当你需要从非组件化的代码访问的组件时,使用Utils.getComponent()函数是强烈不推荐的。对于组件化的代码,你应该在“编译时”依赖声明(如之前注解所示),如果你需要在运行时解决组件的依赖,使用的ComponentManager,你可以通过实现组合接口访问,如组件模块参考里描述。

从wiki页面

组件可以通过编写ScriptService实现wiki页面访问。他们可以使用任何提供的脚本语言访问(velocity, groovy, python, ruby, php等)。

让我们访问sayHello方法:

@Component
@Named("hello")
@Singleton
public class HelloWorldScriptService implements ScriptService
{
   @Inject
   private HelloWorld helloWorld;

public String greet()
   {
       return this.helloWorld.sayHello();
   }
}

注意:我们也可以注入了命名组件,"database",如下

// Or inject a Named Component
@Inject
@Named("database")
private HelloWorld databaseWorld;

使用组件hint(在@Component中的hello),即@Named("hello"),可以从脚本语言访问通过这个名字访问脚本服务。

例如,通过Velocity访问,可以这样写:$services.hello.greet()

通过Groovy:services.hello.greet()

为了让我们的脚本服务可以使用,我们需要把它添加到META-INF/components.txt文件,注册为一个组件:

...
com.acme.internal.HelloWorldScriptService

我们还需要让脚本服务在我们classpath可用,需要在pom.xml文件添加以下内容:

<dependency>
 <groupId>org.xwiki.commons</groupId>
 <artifactId>xwiki-commons-script</artifactId>
 <version>${commons.version}</version>
</dependency>

访问遗留代码

遗留意思是旧的XWiki代码还没迁移为组件。

XWiki数据模型

由于XWiki的数据模型(文档,对象,附件等)存在于又大又老的xwiki-core模块,因为我们不希望添加全部core和所有它的依赖为一个简单的轻量级组件的依赖(这将最终导致循环依赖,maven对这是不允许的),当前的策略,使用一个新的组件架构和old xwiki-core之间的bridge,直到数据模型完成变成一个组件。

总之,这工作方式是基于这样的事实,对于一个组件实现不必在同一个.jar作为接口,不依赖组件接口到实际实现。所以,我们做了几个简单的组件,提供给XWiki文档基本的访问,并在xwiki-core声明类作为这些组件的默认实现。

如果你的组件需要访问XWiki的数据模型,需要使用xwiki-platform-bridge模块。请注意,这些接口是相当小的,所以你不能对旧模型做所有的事情。如果你需要bridge添加一些方法,随时在邮件列表提出。

例如:

@Component
@Singleton
public class DefaultHelloWorld implements HelloWorld
{
    /** Provides access to documents. Injected by the Component Manager. */
    @Inject
    private DocumentAccessBridge documentAccessBridge;

[...]

private String getConfiguredGreeting()
    {
        return documentAccessBridge.getProperty("XWiki.XWikiPreferences", "greeting_text");
    }

查询数据模型

查询可以通过使用QueryManager,如以下代码:

QueryManager queryManager = (QueryManager) componentManager.getInstance(QueryManager.class);
Query query = queryManager.createQuery(xwqlstatement,Query.HQL);
List<Object> results = query.execute();

可以通过注解引用一个ComponentManager,请参考组件模块扩展页面。

XWiki上下文

注意XWiki context已经废弃。这个一种老的方式跟踪当前请求,需要方法到方法传递,就像个枷锁一样遍布每个代码。

在组件的世界里,当前请求信息是在保持Execution Context。这实际上比旧的XWikiContext更加强大,因为它是一个通用的执行环境,持有一个ThreadLocal变量,你可以在随时随地需要的时候创建。

你不需要通过方法调用来手动传递,通过Execution组件管理执行上下文,就像其他XWiki组件一样。

总之,如果你想获得执行上下文(包含了上下文信息),你必须声明的Execution组件(位于xwiki-commons-context模块)并注入,你可以这样写:

/** Provides access to the request context. Injected by the Component Manager. */
@Inject
private Execution execution;
...
private void workWithTheContext()
{
    ExecutionContext context = execution.getContext();
   // Do something with the execution context
}

所有这一切说,我们仍然处于一个过渡阶段,大量的信息仍然只能通过旧的XWikiContext还未转移到执行上下文。因此,你可能仍然需要访问旧的XWiki Context。你可以从Execution Context获得它的引用。如果可以,应该尽量不将其转换为一个XWikiContext,把xwiki-platform-oldcore作为一个依赖,作为一个Map。这样做,你将无法访问的所有属性,如当前用户的名称或URL factory,但是你可以访问任何在XWikiContext map里面的内容。

private void workWithTheContext()
{
    ExecutionContext context = execution.getContext();
    Map<Object, Object> xwikiContext = (Map<Object, Object>) context.getProperty("xwikicontext");
   // Do something with the XWiki context
}

如果你需要访问typed信息,那么最简单的就是使用一个Provider如下:

@Inject
private Provider<XWikiContext> xwikiContextProvider;
...
XWikiContext xcontext = this.xwikiContextProvider.get();

这会提供一个可用XWikiContext(如果没有,当前的ExecutionContext会创建一个)。

从7.2开始,如果在已经有一个情况下你只想获得一个(即你不需要自动创建一个新的),可以使用"readonly"关键字:

@Inject
@Named("readonly")
private Provider<XWikiContext> xwikiContextProvider;
...
XWikiContext xcontext = this.xwikiContextProvider.get();
if (xcontext != null) {
...
}

如果你想不只是使用执行上下文,而是使每一个执行上下文提供的东西,你可以创建ExecutionContextInitializer组件的实现,并填充新创建的执行环境,就像velocity上下文。

外部组件代码

你可以使用任何其他Maven模块中的外部库,只要在你的模块的pom.xml声明正确的依赖关系。

作为一般规则,你不应该使用任何非组件化XWiki代码,因为这样的旧代码的设计会导致最终依赖整个xwiki-core模块,我们需要避免。如果你正在编写的组件由其它模块所需的,那么这将有可能引起最终循环依赖,这将打破整个编译。

如果你需要old core的一些功能,首先考虑的是重写那部分代码并作为一个新的组件,然后在你的代码使用新的组件。你应该在开发者邮件列表提问,这样我们就可以协同设计和实施。

如果需要的工作量太大,你可以尝试创建一个bridge组件,在一个新的模块只需编写接口,让core类从这些接口的默认实现。然后,因为最终xwiki-core,bridge组件和你的组件将在同一类路径,plexus将耦合正确的类。编写这样的bridge时要小心,因为它们是短暂的(因为最终所有的旧代码将通过适当的组件来代替),如果未来真正的组件将具有不同的接口,那么你将不得不重写你的代码适应新的方法名称,或者更糟的是,新组件的逻辑。

部署组件

现在我们已经有一个功能组件,让我们编译、部署它到一个XWiki实例。这里有2种方式。

手动

  • 编译组件,执行mvn install。这会在你的项目的target目录下生成一个JAR。
  • 把组件install到一个XWiki Enterprise实例,只要JAR文件复制到XE_WAR_HOME/WEB-INF/lib下。XE_WAR_HOME为你的XWiki Enterprise的war包部署的位置。

使用Extension Manager

比起手动的方式,使用Extension Manager你不需要重启XWiki实例,这样的话你就不用等待启动的时间。

  • 配置运行中的XWiki实例的本地扩展仓库指向你Maven的本地仓库。编辑xwiki.properties并确保你安装如下设置:

    extension.repositories=local:maven:file://${sys:user.home}/.m2/repository
    extension.repositories=maven-xwiki:maven:http://nexus.xwiki.org/nexus/content/groups/public
    extension.repositories=extensions.xwiki.org:xwiki:http://extensions.xwiki.org/xwiki/rest/

  • 编译你的组件,通过mvn install命令把它部署到你本地的Maven仓库
  • 在你运行的XWiki实例的管理员页面,跳转到Extension Manager(例如 http://localhost:8080/xwiki/bin/admin/XWiki/XWikiPreferences?editor=globaladmin&section=XWiki.AddExtensions),点击Advanced Search然后按照说明输入你的extension id和version

如果你想重新部署一个扩展程序而已经安装过相同版本,Extension Manager是不会让你这样做的。因此,你需要通过Extension Manager先卸载已有的扩展程序。你还需要使用Extension Tweak移除内存中的元数据。

你的组件现在已经准备好提供服务。

参考

  • Component Module
  • 如果你使用Maven install组件遇到一些问题,可以参考编译指南

xwiki开发者指南-编写一个XWiki组件相关推荐

  1. xwiki开发者指南-XWiki API 参考文档

    XWiki开发团队考虑到安全使用(即,保证后向兼容性).我们可以从Java或直接从wiki页面使用一个脚本语言 (Velocity, Groovy, Python, Ruby等)调用API. 作为XW ...

  2. xwiki开发指南1-使用XWiki创建FAQ应用

    本文是按照XWiki官网开发指南-FAQ应用创建,自己翻译总结的版本. 此篇含有大量图片,关于XWiki的开发,将在下一篇讲解. 关于XWiki的部署也会单独写一篇基于docker的部署文. 使用XW ...

  3. 用vue 编写一个日历组件(非常详细-让日历简单起来)

    相信有不少小伙伴和我一样一提到日历就脑壳疼,然后去网上搜索好用的日历组件,如element-ui什么的,但是日历毕竟是别人开发出来的, 和自己家ui设计出来的功能样式毕竟不能100%相似,所以这个时候 ...

  4. react学习笔记(二)编写第一个react组件

    继续上一节课的内容,打开App.js:会看到如下代码: import React, { Component } from 'react'; //在此文件中引用React,以及reat的组件类 impo ...

  5. 在Excel中引用Visual Studio.NET 2008编写的COM组件

    最近在研究Office编程,发现了这个领域也是非常非常的有趣,而且,市场也是大大的有.今天遇到的一个课题是:用VS2008编写一个COM组件,并在Excel中用VBA调用这个COM组件实现一些功能.以 ...

  6. firefox附加组件开发者指南(五)——创建一个firefox扩展(上)

    本章篇幅较长,分上下两部分发布,本章中的图和清单4在原网站中没有,因此这里也没有,感兴趣的可以去原文补充.下面是译文. 至今为止,各个章节中独立的介绍了相关的技术--XUL.javascript.CS ...

  7. XWiki开发指南3-自定义扩展jar包

    简介 本文将介绍如何编写一个提供给后台接口并使用脚本在前端页面调用. 使用maven下载XWiki提供的模板项目 保证你的maven已经安装,然后执行命令 mvn archetype:generate ...

  8. 用C#编写一个进程外的COM组件示例代码讲解

    代码的链接在<用C#编写一个进程外的COM组件>,小技巧:如果你要同时看示例代码和讲解的话,可以用浏览器分别打开示例代码和这篇文章,然后使用Windows提供的纵向平铺窗口功能就可同时看两 ...

  9. 编写一个头像裁剪组件(一)

    需求是编写一个头像剪裁工具再封装成为一个组件,然后根据功能开始逐步编写代码:先是上传图片 => 预览图片 => 编辑图片. 刚开始没有去考虑兼容性的问题,直接编写upload部分的代码,参 ...

最新文章

  1. CSS background 之设置图片为背景技巧
  2. java.lang.ClassCastException: java.math.BigDecimal cannot be cast to java.lang.Integer
  3. Xamarin XAML语言教程构建进度条ProgressBar
  4. 开博拉~~~庆祝一下,哈~~~
  5. apeach和php合作原理,apache与php工作原理分析
  6. idea 类注释,方法注释设置
  7. 图像处理工具包ImagXpress中如何设置上下文菜单
  8. 考前建议:好好看一下 《网络工程师考试案例动手实验营》附录A
  9. 1月31日 资源网站(素材模板)
  10. 【Arthas】Arthas 导出堆栈信息
  11. mysql在windows启动_MySQL笔记:mySQL在windows环境启动
  12. 解决Eclipse开发工具Debug调试JDK源码无法查看变量值
  13. 如何使用JdbcTemplate实现数据的增删改查?
  14. 《计算机操作系统》思维导图
  15. Linux上Meson安装及使用
  16. 使用Cisco Packet Tracer之图解无线网络全网互联
  17. android 系统重新安装程序,Android应用程序更新时的安装与卸载(调用系统方法)...
  18. CVPR 2022 Oral | MLP进军底层视觉!谷歌提出MAXIM:刷榜多个图像处理任务,代码已开源!...
  19. git更换主分支master
  20. 聚集索引和非聚集索引详解 (zhuang)

热门文章

  1. 期刊模板-如何去除左下角的横线
  2. 火了几年的大前端,现在怎么样了?
  3. canvas 图像的平铺
  4. BeCJK输入法移植
  5. 后端给图片加水印的方法
  6. 第四届“图灵杯”NEUQ-ACM程序设计人赛真题重现
  7. 谢文东的一些话,很有道理
  8. 万历十五年 . 读书笔记
  9. eclipse 配置 wildfly
  10. 科罗拉多大学波尔得分校计算机科学,科罗拉多大学波尔得分校副教授Dan Zhang:基于线性规划的近似动态规划方面的一些最新成果...