使用Forge 2进行插件开发 - 基础入门

发布者:    |       JBoss Forge

今天我有幸从Forge团队乔治·加斯塔迪(George Gastaldi)那里接受了关于Forge插件开发在Eclipse中的一些专家指导,我对这个过程如此简单而感到印象深刻,因此我将一步步地描述这个过程,以便你也能体验到编写Forge插件带来的乐趣!本指南是从一个完全的Forge新手的角度编写的,所以你可以放心,它已经非常努力地捕捉到了所有重要的细节。此外,尽管插件本身并不会做很多事情,但我们将尝试涵盖所有基础知识,这可能会为你提供一个坚实的基础,以便实现更复杂的功能。

设置你的环境

我强烈建议你使用Forge的最新trunk版本,所以打开你的终端窗口,首先从GitHub克隆以下存储库

https://github.com/forge/furnace

https://github.com/forge/furnace-cdi

https://github.com/forge/forge-core

https://github.com/jbosstools/jbosstools-forge

一旦你有了它们,就使用Maven构建每个项目,确保跳过测试。你将需要Maven 3.1或更高版本来完成此步骤

cd furnace
mvn clean install -Dmaven.test.skip=true

如果一切顺利,你将在最后得到BUILD SUCCESS消息

继续构建其他三个项目(furnace-cdi, forge-codejbosstools-forge)按照相同的Maven命令顺序。

安装Forge

一旦所有项目都成功构建,就到了安装Forge的时候了。打开forge-core/dist/target目录,在那里你可以找到包含Forge分发版本的zip文件

这里你可以选择,你可以使用(较小的)分发zip文件,它没有预打包所有核心插件,或者你可以使用较大的离线分发版本,该版本包含打包的插件。现在,我建议你使用离线分发版本,将其解压缩到自己的文件夹中。我通常喜欢将这类工具安装到一个专门的app文件夹中,如果你使用的是基于*nix的操作系统,我建议创建一个符号链接。

完成之后,你需要创建一个FORGE_HOME环境变量,指向此目录,并将$FORGE_HOME/bin目录添加到你的路径中(这会让你可以从任何地方执行 forge shell)。这是一个操作系统特定的步骤,所以你需要自己完成。完成之后,从命令提示符运行forge命令。

(注意:如果你没有安装离线分发版,你会被提示安装核心插件。当你启动 forge 时,它会尝试安装最新发布的版本,但在我们的例子中,我们想安装我们刚刚自己构建的工件,所以为了做到这一点,我们必须首先运行forge -i core,2.0.1-SNAPSHOT——或者直接使用离线分发版,这样会节省很多麻烦。)

Forge 会下载一大堆东西,然后提示你确认安装,所以选择Y并再次按Enter

最终,安装将完成,你将坐在 Forge 命令提示符下

从这里,你可以按tab键来查看可用的命令

我们已经成功设置了 Forge shell!现在让我们通过输入exit然后按 enter 退出,然后我们将介绍如何在 Eclipse 中安装 JBoss Tools Forge 插件的步骤。

安装 Forge Eclipse 插件

在写作本文时,我使用的是 Eclipse 的最新发布版本 Kepler SR1。如果你使用的是较旧版本的 Eclipse,我建议你更新到最新版本,否则结果可能不可预测。安装 Eclipse 插件的第一步是启动 Eclipse,然后转到 帮助 -> 安装新软件… 菜单选项

然后点击 添加 按钮,然后在弹出的窗口中点击 存档

浏览到jbosstools-forge/site/target目录,选择forge.site.XXXzip 文件,然后点击 OK

再次在 添加存储库 窗口中点击 OK,然后选择 JBoss Tools Forge 夜间构建更新站点 旁边的复选框,然后点击 下一步 按钮

你将需要审查安装详情,所以再次点击 下一步 按钮。你将被要求审查和接受许可协议的条款,所以在接受它们后点击 完成 按钮,Eclipse 将继续安装插件。如果你收到关于未签名内容的警告,不要慌张;这只是个警告,你可以在这里点击 OK。安装完成后,Eclipse 将通知你需要重新启动,所以点击 Yes 按钮。Eclipse 重新启动后,你可以通过按 Ctrl + 5 来确认 Forge 插件已成功安装,这会弹出一个以下窗口

如果你在按 Ctrl + 5 后可以看到 Forge 命令窗口,那么恭喜你走到了这一步!接下来,我们将继续创建我们的 Forge 插件项目。

创建 Forge 插件项目

当涉及到创建新的插件时,Forge 做了很多繁重的工作。让我们通过在 Eclipse 中打开 Forge 命令窗口(Ctrl + 5),然后向下滚动直到找到 项目/生成 - 项目:新建 选项来为我们的插件创建初始项目结构。

点击 项目:新建 选项会弹出一个对话框,允许我们为插件项目输入一些基本信息。填写项目名称、顶层包、版本、最终名称和项目位置。选择 Forge 插件 作为项目类型,并保留 Maven 作为默认构建系统,然后点击下一步。

在下一屏中,点击旁边的复选框 创建API、Impl、SPI、测试和插件模块,并从插件列表中选择以下插件

org.jboss.forge.addon:projects

org.jboss.forge.addon:ui

org.jboss.forge.furnace.container:cdi

选择这些插件后,点击 完成 按钮以生成新项目。

几秒钟后,您应该在Eclipse项目资源管理器中看到以下新项目

我们的新插件项目已创建!接下来,我们将开始添加一些基本功能。

让插件做些事情

这里是激动人心的部分。让我们先在impl项目中创建一个新类,位于com.acme.helloworld.ui包中的HelloWorldUICommand。一旦创建了此类,编辑其代码,使其扩展AbstractUICommand:

package com.acme.helloworld.ui;

import org.jboss.forge.addon.ui.command.AbstractUICommand;

public class HelloWorldUICommand extends AbstractUICommand {

}

完成之后,让Eclipse添加未实现的方法

package com.acme.helloworld.ui;

import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.result.Result;

public class HelloWorldUICommand extends AbstractUICommand {

    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        // TODO Auto-generated method stub
        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        // TODO Auto-generated method stub

    }

}

让我们写一点最基础的代码,以便我们可以安装插件并尝试它。通过添加以下代码实现execute()方法

package com.acme.helloworld.ui;

import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractUICommand {

    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        // TODO Auto-generated method stub
        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        return Results.success("Hello World");
    }

}

完成后,保存类并运行mvn clean install在你的插件项目的父目录中

然后,在Eclipse中,选择helloworld-addon项目(或你叫什么名字),按Ctrl + 5显示Forge命令窗口,并选择 插件/管理 - 安装插件 命令

安装插件 窗口中保持默认值不变,并点击 完成 按钮

如果一切顺利,你应该会在屏幕右下角看到一个弹出窗口,确认插件已成功安装。我们可以通过按Ctrl + 5并向下滚动找到我们的新命令来立即测试我们的新插件

单击该命令应会给我们另一个弹出窗口,显示我们的hello world消息

创建用户界面元素

下一步是创建一些用户界面元素,用于提示用户输入。我们将从简单地注入一个简单的文本输入控件并将其添加到我们的插件的对话框屏幕开始 - 这里是完整的代码列表

package com.acme.helloworld.ui;

import javax.inject.Inject;

import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractUICommand {

    @Inject UIInput<String> input;
    
    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        builder.add(input);
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        return Results.success("Hello World");
    }

}

与前述方法相同重新构建项目,并从Forge命令屏幕(Ctrl + 5并选择 插件/管理 : 安装插件)重新安装插件。再次按Ctrl + 5并选择HelloWorldUICommand,你现在应该看到以下对话框窗口

我们可以通过添加一个@WithAttributes注解来进一步自定义我们的输入控件 - 添加以下导入

import org.jboss.forge.addon.ui.metadata.WithAttributes;

然后,将@WithAttributes注解添加到UIInput

@Inject @WithAttributes(label = "Name", required = true, description = "Enter your name")  
    UIInput<String> input;

可以通过设置成员值来定制控件

label- 输入的标签

required- 使输入成为必填字段

description- 当鼠标悬停在字段上时的描述。

保存你的类,然后按照前面的步骤构建和重新安装插件。使用Ctrl + 5执行插件命令,你应该现在看到这个

我们现在可以开始看到如何简单地为我们的插件构建用户界面。让我们进一步扩展它,添加一个允许用户选择Maven依赖项的控件。在这个例子中,我将使用PicketLink项目作为我们的小白鼠。我们需要@Inject一个DependencyResolver来定位我们感兴趣的依赖项,以及一个UISelectOne控件,允许用户选择依赖项之一。将以下代码添加到你的类中

@Inject DependencyResolver dependencyResolver;    
    
@Inject @WithAttributes(label = "Version", required = true, description = "Select the version of PicketLink") 
private UISelectOne<Coordinate> version;    
    
@Override
public void initializeUI(UIBuilder builder) throws Exception {
    builder.add(input);
        
    DependencyQuery query = DependencyQueryBuilder
            .create("org.picketlink:picketlink-api")
            .setFilter(new NonSnapshotDependencyFilter());

    List<Coordinate> coordinates = dependencyResolver.resolveVersions(query);
    version.setValueChoices(coordinates);
    builder.add(version);        
}

TheDependencyQuery用于声明我们将用于定位所需依赖项的准则,以及NonSnapshotDependencyFilter参数告诉查询我们只对非快照版本感兴趣。Coordinate类用于表示特定的Maven依赖项,并包含依赖项的groupId, artifactId等属性值。我们将Coordinate列表填充到选择控件中,并通过builder.add()方法将控件添加到表单中。完成之后,完整的代码列表应如下所示

package com.acme.helloworld.ui;

import java.util.List;

import javax.inject.Inject;

import org.jboss.forge.addon.dependencies.Coordinate;
import org.jboss.forge.addon.dependencies.DependencyQuery;
import org.jboss.forge.addon.dependencies.DependencyResolver;
import org.jboss.forge.addon.dependencies.builder.DependencyQueryBuilder;
import org.jboss.forge.addon.dependencies.util.NonSnapshotDependencyFilter;
import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.input.UISelectOne;
import org.jboss.forge.addon.ui.metadata.WithAttributes;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractUICommand {
    @Inject @WithAttributes(label = "Name", required = true, description = "Enter your name")  
    UIInput<String> input;

    @Inject DependencyResolver dependencyResolver;    
    
    @Inject @WithAttributes(label = "Version", required = true, description = "Select the version of PicketLink") 
    private UISelectOne<Coordinate> version;    
    
    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        builder.add(input);
        
        DependencyQuery query = DependencyQueryBuilder
                .create("org.picketlink:picketlink-api")
                .setFilter(new NonSnapshotDependencyFilter());

        List<Coordinate> coordinates = dependencyResolver.resolveVersions(query);
        version.setValueChoices(coordinates);
        builder.add(version);        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        return Results.success("Hello World");
    }
}

重新构建并重新安装插件,然后从Forge命令窗口中调用它 - 你应该会看到如下所示的内容

我们可以利用这些新发现的力量来修改实际项目。让我们首先修改我们的插件类,使其扩展AbstractProjectCommand而不是AbstractUICommand:

public class HelloWorldUICommand extends AbstractProjectCommand {

这将导致Eclipse对未实现的方法发出警告,因此请使用Eclipse生成它们

    @Override
    protected boolean isProjectRequired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    protected ProjectFactory getProjectFactory() {
        // TODO Auto-generated method stub
        return null;
    }

TheisProjectRequired()方法用于确定此插件是否需要项目来执行。由于这正是我们想要的,因此在这里我们将返回true的值

    @Override
    protected boolean isProjectRequired() {
        return true;
    }

ThegetProjectFactory()方法提供了一个对当前选定项目的引用。要实现此方法,我们首先需要在@Inject一个ProjectFactory对象到我们的插件类中

@Inject ProjectFactory projectFactory;

然后,在方法体中简单返回此值

    @Override
    protected ProjectFactory getProjectFactory() {
        return projectFactory;
    }

到目前为止一切顺利!现在,由于我们的目标是向项目添加一些依赖项,我们需要以某种方式从我们的UISelectOne控制中获取选定的Coordinate值,并使用它来向当前项目添加依赖项。幸运的是,Forge为我们提供了实现这一目标的所有工具 - 首先我们将注入一个DependencyInstaller到我们的类中

@Inject DependencyInstaller dependencyInstaller;

然后,在execute()方法中,我们将使用一个DependencyBuilder来根据Coordinate值创建一个Dependency引用,该值包含在UISelectOne控件中,之后我们将告诉DependencyInstaller将其安装到所选项目Coordinate

DependencyBuilder builder = DependencyBuilder.create();
builder.setCoordinate(version.getValue());

dependencyInstaller.install(getSelectedProject(context), builder);

完整的代码列表现在应如下所示

package com.acme.helloworld.ui;

import java.util.List;

import javax.inject.Inject;

import org.jboss.forge.addon.dependencies.Coordinate;
import org.jboss.forge.addon.dependencies.DependencyQuery;
import org.jboss.forge.addon.dependencies.DependencyResolver;
import org.jboss.forge.addon.dependencies.builder.DependencyBuilder;
import org.jboss.forge.addon.dependencies.builder.DependencyQueryBuilder;
import org.jboss.forge.addon.dependencies.util.NonSnapshotDependencyFilter;
import org.jboss.forge.addon.projects.ProjectFactory;
import org.jboss.forge.addon.projects.dependencies.DependencyInstaller;
import org.jboss.forge.addon.projects.ui.AbstractProjectCommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.input.UISelectOne;
import org.jboss.forge.addon.ui.metadata.WithAttributes;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractProjectCommand {
    
    @Inject ProjectFactory projectFactory;

    @Inject @WithAttributes(label = "Name", required = true, description = "Enter your name")  
    UIInput<String> input;
    
    @Inject DependencyInstaller dependencyInstaller;

    @Inject DependencyResolver dependencyResolver;    
    
    @Inject @WithAttributes(label = "Version", required = true, description = "Select the version of PicketLink") 
    private UISelectOne<Coordinate> version;    
    
    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        builder.add(input);
        
        DependencyQuery query = DependencyQueryBuilder
                .create("org.picketlink:picketlink-api")
                .setFilter(new NonSnapshotDependencyFilter());

        List<Coordinate> coordinates = dependencyResolver.resolveVersions(query);
        version.setValueChoices(coordinates);
        builder.add(version);        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        DependencyBuilder builder = DependencyBuilder.create();
        builder.setCoordinate(version.getValue());

        dependencyInstaller.install(getSelectedProject(context), builder);
        
        return Results.success("Hello World");
    }

    @Override
    protected boolean isProjectRequired() {
        return true;
    }

    @Override
    protected ProjectFactory getProjectFactory() {
        return projectFactory;
    }
}

重新构建并重新安装您的插件,然后在Eclipse中选择一个项目,并通过Forge命令窗口调用我们的插件。请记住,我们将向您选择的项目添加一些新的依赖项,因此请确保它不是太重要!我建议您使用插件项目本身,因为删除依赖项之后非常容易。为名称字段输入任何值(由于我们已将其标记为必填项,我们被迫至少输入一些内容),并从版本控件中选择一个依赖项

单击完成按钮,你应该会看到Hello World弹出窗口短暂出现,确认我们的插件已成功运行。要检查它实际上做了些什么,请打开项目的pom.xml在编辑器中,你应该会注意到Forge通过在<dependencyManagement>部分声明来添加所选依赖项

并将依赖项本身添加到<dependencies>部分声明来添加所选依赖项

恭喜你,你已经走到了最后!你现在已经创建了一个有用的Forge插件,可以将依赖项添加到现有项目中。希望这个指南通过提供Forge插件架构的高级概述和创建新Forge插件的入门指南有所帮助。

总结

在本指南中,我们介绍了构建和安装Forge Shell和Forge Eclipse插件的方法,然后学习了如何创建新的Forge插件项目。我们还学习了如何构建和安装我们的插件,创建自定义对话框窗口,并使我们的插件对现有项目进行更改。如果您想了解更多关于Forge的信息,以下资源可供参考

Forge网站:http://forge.jboss.org/

文档:http://forge.jboss.org/docs/index.html

源代码: https://github.com/forge

感谢阅读!


返回顶部