Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Forge Core Framework APIs and Implementation

Octocat-spinner-32 addon-manager Skip tests during addon installation. April 10, 2014
Octocat-spinner-32 addons Immutable repositories are no longer avoided April 11, 2014
Octocat-spinner-32 bom Added <relativePath/> to BOM to avoid maven warnings April 14, 2014
Octocat-spinner-32 bootstrap [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 configuration FORGE-1754: Added test with getLong() April 10, 2014
Octocat-spinner-32 convert [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 core [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 database-tools Showing package name where entities were generated April 10, 2014
Octocat-spinner-32 dependencies [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 dev-tools-java [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 dist [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 environment [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 facets FORGE-1699: Performance enhancements for Project location April 08, 2014
Octocat-spinner-32 git [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 javaee Enums cannot appear in targetEntity list April 16, 2014
Octocat-spinner-32 maven Add a SetVersionCommand for the Java Compiled Projects April 12, 2014
Octocat-spinner-32 parser-java Using Roaster 2.2.1-SNAPSHOT April 16, 2014
Octocat-spinner-32 parser-json Don't check file modification timestamp immediately - just start out … April 08, 2014
Octocat-spinner-32 parser-xml [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 projects Fix ProjectFactoryImpl.findProject() bug April 12, 2014
Octocat-spinner-32 resources JavaResource objects should use higher precision staleness mechanisms. April 09, 2014
Octocat-spinner-32 scaffold [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 shell FORGE-1761: Added name() to @WithAttributes April 14, 2014
Octocat-spinner-32 templates [maven-release-plugin] prepare for next development iteration April 05, 2014
Octocat-spinner-32 text Using classloader to load test resources April 14, 2014
Octocat-spinner-32 ui Fixed test failures due to commit 35ad933 April 14, 2014
Octocat-spinner-32 .gitignore Added projects documentation July 31, 2013
Octocat-spinner-32 .travis.yml Added travis-ci.org descriptor file for CI January 18, 2013
Octocat-spinner-32 CONTRIBUTING.md Fixed JIRA filter link March 07, 2014
Octocat-spinner-32 LICENSE Added EPL license August 13, 2012
Octocat-spinner-32 README.asciidoc Update README.asciidoc April 05, 2014
Octocat-spinner-32 eclipse-code-formatter-profile.xml Changed code formatter profile name May 04, 2012
Octocat-spinner-32 forge-install.bsh Forge install script supports windows. January 14, 2014
Octocat-spinner-32 pom.xml Furnace version = project.version April 05, 2014
README.asciidoc

JBoss Forge 2.0

The fastest way to build applications, share your software, and enjoy doing it.

What’s new and noteworthy?

  • Addons: What were previously called "Plugins" in Forge 1, are now "Addons" in Forge 2. This decision was made to clear up confusing verbiage like, "Plugin X has N Plugins" (due to the org.jboss.forge.plugins.Plugin interface.)

  • Modular Container fully rewritten: The Forge runtime is now a fully functional Java module system based on JBoss Modules (The same engine behind JBoss AS 7+ and JBoss EAP). This means you may now pick-and-choose which addons are important for you.

  • Smaller, leaner, and faster: Forge 2 now sports a slimmer four megabyte download size, and starts up in under three seconds. (Compared to upwards of 10+ seconds for Forge 1)

  • Better IDE Integration: Forge 2 addons have been de-coupled from the command line, meaning you can create addons that run as wizards in the IDE, as well as commands in the shell - the same code works in both environments.

We are currently in the process of migrating Forge 1 to Forge 2, so expect to find some Forge 1 functionality missing in the early versions of Forge 2.

See the JavaDocs here

Download Forge 2:

Forge 2 is packaged inside an Eclipse plugin and also as a standalone ZIP file. They are independent of each other. It is worth mentioning that the Eclipse plugin does not support access to shell yet.

Get Started with the Command line tools:

Getting started with the command line tools is easy:

    forge

Forge is now ready to go.

Install addons (not needed if using the offline zip)

Install the required addons by running the following commands:

    forge --install groupId:artifactId,version
  • Forge will install the required dependencies for each addon.

Important
Make sure your settings.xml is properly configured to use the Maven central repository or, if you wish to use a SNAPSHOT version from a core addon, the JBoss Nexus Repository, otherwise the installation will fail. Instructions on how to setup your settings.xml can be found on https://community.jboss.org/wiki/MavenGettingStarted-Developers

If you wish to install the core addons including the shell, "Project: New", the "Java EE" commands, and all other provided functionality, you should run the following command:

    forge --install core

If you only wish to install the prototype Forge 2 Shell based on Aesh, run the following command instead:

    forge --install shell

If you wish to remove any addon, you can use the following command:

    forge --remove groupId:artifactId,version

Get Started with the Forge 2 Eclipse Plugin:

Forge 2 is known to work with Eclipse Kepler (4.3) or higher versions.

This plugin starts the Forge 2 Container and your installed addons, so you can use them directly in your workspace

  • Press Ctrl + 5 to show the installed addons that you may interact with (these addons use the UI addon, hence providing a user interface - see Developing an UI Addon for more details).

Available addons

Addon name Included in Eclipse Plugin ?

Addon Manager

yes

Addons

yes

Configuration

yes

Convert

yes

Dependencies

yes

Dev Tools - Java

yes

Environment

yes

Facets

yes

Git

yes

Java EE

yes

Maven

yes

Parser Java

yes

Parser Json

no

Parser XML

yes

Projects

yes

Resources

yes

Scaffold

yes

Shell

yes

Templates

yes

Text

yes

User Interface (UI)

yes

Developing an Addon

Forge addons are simple Maven projects with a special classifier "forge-addon". This classifier is used while installing an addon so the Furnace container can calculate its dependencies, freeing you from Classloader hell.

One of the most important things to know about developing a Forge addon, is that the Furnace runtime container (the core of Forge), is actually an embeddable, modular Java container. This means that each addon has its own ClassLoader and that addons share classes from each other, in addition to supplying their own local classes. Furnace builds a graph of addon dependencies at runtime, and automatically calculates which addons should see classes from other addons.

For now, however, just treat your first addon as if it were any other Java project. The differences between a "modular" and "traditional" environment are not as great as you might think, and the Furnace development model has been created in a way that should make these differences seem natural, almost transparent.

Create a Maven project

Forge Addons must be JARs published with a forge-addon classifier. Add this plugin configuration to your pom.xml:

<build>
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
            <execution>
               <id>create-forge-addon</id>
               <phase>package</phase>
               <goals>
                  <goal>jar</goal>
               </goals>
               <inherited>false</inherited>
               <configuration>
                  <classifier>forge-addon</classifier>
               </configuration>
            </execution>
         </executions>
      </plugin>
   </plugins>
</build>

In order to use CDI and services from other addons in your addon, you’ll need to reference the Furnace CDI container addon as a dependency your pom.xml file:

<dependency>
   <groupId>org.jboss.forge.furnace.container</groupId>
   <artifactId>cdi</artifactId>
   <classifier>forge-addon</classifier>
   <scope>provided</scope>
</dependency>
Caution
Addon dependencies MUST be declared in the Maven pom.xml that produces your forge-addon classified artifact; otherwise, Furnace will NOT use this dependency as a forge-addon. Instead, addon dependencies declared via transitive dependencies will be included as local JAR files and re-bundled with your addon. More than likely, re-bundling a forge-addon in your Addon is NOT what you want.

Your complete POM should now look something like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.example</groupId>
   <artifactId>example</artifactId>
   <version>0.0.1-SNAPSHOT</version>

   <name>My First Addon</name>

   <dependencies>
      <dependency>
         <groupId>org.jboss.forge.furnace.container</groupId>
         <artifactId>cdi</artifactId>
         <classifier>forge-addon</classifier>
         <scope>provided</scope>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <executions>
               <execution>
                  <id>create-forge-addon</id>
                  <phase>package</phase>
                  <goals>
                     <goal>jar</goal>
                  </goals>
                  <inherited>false</inherited>
                  <configuration>
                     <classifier>forge-addon</classifier>
                  </configuration>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
</project>

Add behavior to your addon

A service is implemented as a POJO (Plain Old Java Object):

public class ExampleServiceImpl
{
   public String doSomething() {
        // Do stuff...
   }
}

However, best practices favor creating a service interface, otherwise consumers will be required to request your specific service implementation. For example:

public interface ExampleService
{
   public String doSomething();
}

Then simply implement the service interface:

public class ExampleServiceImpl implements ExampleService
{
   public String doSomething() {
        // Do stuff...
   }
}

Re-use functionality from other addons:

Forge has a modular architecture that enables you to re-use functionality from other addons, directly in your own addon code. In order to achieve this, you must add addon-dependencies in your pom.xml file.

<project>
   ...

   <dependencies>
      <!-- Addon Dependencies -->
      <dependency>
         <groupId>org.jboss.forge.addon</groupId>
         <artifactId>resources</artifactId>
         <classifier>forge-addon</classifier>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.jboss.forge.addon</groupId>
         <artifactId>ui</artifactId>
         <classifier>forge-addon</classifier>
         <scope>provided</scope>
      </dependency>

      <!-- Furnace Container -->
      <dependency>
         <groupId>org.jboss.forge.furnace.container</groupId>
         <artifactId>cdi</artifactId>
         <classifier>forge-addon</classifier>
         <scope>provided</scope>
      </dependency>
   </dependencies>

   ...
</project>

What scope should my addon dependencies be?

There is a simple rule that will make this an easy decision:

"compile if it shows, provided if nobody knows."

To explain, if you never publicly expose types (classes, interfaces, etc…) from another addon in the outward-facing APIs of your addon, then you should include that addon as provided scope. If you do, however, expose classes from that addon in the public APIs of your code, then that addon should be labeled as compile scope (default,) which means that this dependency will be exported to consumers that depend on your addon.

Addon dependencies may also be made optional if consumers of your addon should be able to choose whether or not certain functionality is enabled, or if your addon behaves differently when other addons are already deployed to the container.

The following chart explains this in detail. Assume that our addon depends on the resources addon, which provides the ResourceFactory and FileResource classes:

Example Scope should be Explanation
public class InternalExample {
   @Inject private ResourceFactory factory;

   public void doSomething(File file) {
      Resource<?> r = factory.create(file);
      System.out.println("New resource: " + r)
   }
}

provided

Consumers of your addon never see classes or interfaces from the resources addon; it is only used internally as an implementation detail.

public class ExposedExample {
   public Resource<?> doSomething(File file) {
      Resource<?> r = factory.create(file);
      return r;
   }
}

compile

Consumers of your addon require classes from the resources addon to interact with your code, since it has been used in the public APIs of your classes.

public class TransitiveExample {
   public void doSomething(Facet<?> f) {
      System.out.println("I got a facet! " + f);
   }
}

compile

Consumers of your addon require classes from the facets addon to interact with your code, which is an exported dependency of the resources addon.

The Facet class comes from the facets addon and is used in the public APIs of your addon.

Test your addon

One of the most important concepts of writing a Forge addon is writing tests using the Furnace test harness. This allows you to test your code in an actual Furnace environment, and verify that things are behaving as expected. Typically we suggest using a separate project to test your addon in order to keep concerns separate, which tends to lead to cleaner code and fewer surprises.

For simplicity’s sake, we’ll assume that your addon uses the default Furnace container (org.jboss.forge.furnace.container:cdi).

Set up the test-harness in your build descriptor (pom.xml)

Add the following dependencies to your pom.xml file if they are not already there. Make sure that the Furnace versions are the same as the rest of your project.

<dependency>
   <groupId>org.jboss.forge.furnace.test</groupId>
   <artifactId>furnace-test-harness</artifactId>
   <version>FURNACE_VERSION</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.jboss.forge.furnace.test</groupId>
   <artifactId>arquillian-furnace-classpath</artifactId>
   <version>FURNACE_VERSION</version>
   <scope>test</scope>
</dependency>

If you are writing tests in a separate project or sub-project, you should also add a dependency to your addon, or to the addon you wish to test (you can test anything you like.)

<dependency>
   <groupId>com.example</groupId>
   <artifactId>example</artifactId>
   <classifier>forge-addon</classifier>
   <version>YOUR_VERSION</version>
   <scope>test</scope>
</dependency>

Write your first test

Now, you’ll need to create a test class with the following layout, using the standard JUnit test APIs:

package org.example;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.forge.arquillian.archive.ForgeArchive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class ExampleFurnaceTest {

   @Deployment
   public static ForgeArchive getDeployment() {
      ForgeArchive archive = ShrinkWrap.create(ForgeArchive.class);
      return archive;
   }

   @Test
   public void testSomething() throws Exception {
      Assert.fail("Not implemented");
   }
}

Then you’ll need to add some configuration so that your addon will be deployed to the test environment. This is done using the @AddonDependency annotation. You’ll also need to add an addon dependency link from your test case to your addon (otherwise the test case will not be able to use any of your addon’s classes or services.)

@RunWith(Arquillian.class)
public class ExampleFurnaceTest {

   @Deployment
   @Dependencies({
       @AddonDependency(name = "org.example:example", version = "YOUR_VERSION")
   })
   public static ForgeArchive getDeployment() {
      ForgeArchive archive = ShrinkWrap.create(ForgeArchive.class)
         .addBeansXML()
         .addAsAddonDependencies(
            AddonDependencyEntry.create("org.example:example", "YOUR_VERSION"),
         );
      return archive;
   }

   @Test
   public void testSomething() throws Exception {
      Assert.fail("Not implemented");
   }
}
Note
The @Dependencies annotation is used to specify addons that must be deployed before the Addon-Under-Test is deployed in Furnace. The AddonDependencyEntry.create(...) method is used to specify addons that the Addon-Under-Test depends on.

Now that the test case deploys and depends on your addon, you may access services from it via injection:

@RunWith(Arquillian.class)
public class ExampleFurnaceTest {

   @Deployment
   @Dependencies({
       @AddonDependency(name = "org.example:example", version = "YOUR_VERSION")
   })
   public static ForgeArchive getDeployment() {
      ForgeArchive archive = ShrinkWrap.create(ForgeArchive.class)
         .addBeansXML()
         .addAsAddonDependencies(
            AddonDependencyEntry.create("org.example:example", "YOUR_VERSION"),
         );
      return archive;
   }

   @Inject
   private ExampleService service;

   @Test
   public void testSomething() throws Exception {
      Assert.assertNotNull(service);
      Assert.assertNotNull(service.doSomething());
   }
}

This is the basic premise of using the test-harness. For detailed examples, take a look at some of the existing Forge test cases in our github repository.

Note
The version parameter in @AddonDependency and in the AddonDependencyEntry.create(...) method are optional. By not specifying them means that the test harness will attempt to find the version based on the tests' build descriptor (pom.xml). In this case, if the dependent addon is not present in the tests' build descriptor, the test execution should fail.

Install your addon in the local maven repository:

Depending on the Forge environment in which you are running, installation steps will differ.

For Eclipse

Open the Forge quick-assist menu, select either "Build and install an Addon" or "Install an addon" to build and install your project, or install a pre-built maven artifact.

For the Shell
    mvn clean install

Run

    ./forge --install yourgroupId:artifactId,version
Warning
This coordinate is NOT the same as Maven’s. You MUST use a comma (,) between the artifactId and the version.
Something went wrong with that request. Please try again.