SeamFramework.orgCommunity Documentation

Forge Reference Guide

Authors & Contributors

1.0.0.Alpha2


Introduction
1. Installation
1.1. Installing a distribution download
2. Generating a basic Java EE web-application
2.1. First steps with Scaffolding
3. Developing a Plugin
3.1. Referencing the Forge APIs
3.1.1. Using Forge
3.1.2. With Maven)
3.2. Implementing the Plugin interface
3.3. Naming your plugin
3.4. Ensure all required classes are on the CLASSPATH
3.5. Make your Plugin available to Forge
3.6. Add commands to your plugin
3.6.1. Default commands
3.6.2. Named commands
3.7. Understanding command @Options
3.7.1. --named options
3.7.2. Ordered options
3.7.3. Combining --named and ordered options
3.7.4. Option attributes and configuration
3.8. Piping output between plugins
A. Affiliation

How many times have you wanted to start a new project in Java EE, but struggled to put all the pieces together?

Has the Maven archetype syntax left you scratching your head? Everyone else is talking about cool new tools in other languages or frameworks, and you're left thinking, "I wish it were that easy for me." Well, there's good news: You don't have to leave Java EE just to find a developer tool that makes starting out simple. JBoss Forge is heating up Java EE, and is ready to work it into a full-fledged project.

In addition to being a rapid-application generation tool, Forge is also an incremental enhancement tool that lets you to take an existing Java EE projects and safely work-in new functionality. Forge comprehends your entire project, including the abstract structure of the files, and can make intelligent decisions of how and what to change.

Whether you want to get your startup going today, or make your big customers happy tomorrow, Forge is a tool you should be looking at.

Installing Forge is a relatively short process, and this guide will take you through the fundamentals (providing links to external materials if required;) however, if you encounter any issues with this process, please ask in the Forge Users mailing list, or if you think something is wrong with this guide, report a defect under "Documentation".

Follow these steps to install a Forge distribution:

  1. Ensure that you have already installed a Java 6+ JDK and Apache Maven 3.0+
  2. Download and Un-zip Forge (or a recent snapshot build) into a folder on your hard-disk, this folder will be your FORGE_HOME
  3. Add '$FORGE_HOME/bin' to your path (windows, linux, mac osx)
  4. Open a command prompt and run 'forge'

That's it, you've now got Forge installed, but what to do next?

There are a few things you should probably check-out. If you are confused at any time, try pressing <TAB>. For instance, if you have not yet seen the Forge built-in commands, you may either press <TAB> to see a list of the currently available commands, or get a more descriptive list by typing:

$ list-commands --all

You may also use the 'help' command for more detailed information about available Forge, a plugin, or a command.

$ help {plugin-name} {command-name}

For the most part, people interested in Forge are likely interested in creating web-applications. Thusly, this chapter will overview the basic steps to generate such an application using Forge.

Assuming you have already completed the steps to install Forge, the first thing you'll need to do is download and install JBoss Application Server 6.0. This server will host your application once is is built.

Next, follow these steps to create your skeleton web-application; be sure to replace any {ARGS} with your own personal values. Also keep in mind that while typing commands, you may press <TAB> at any time to see command completion options:

  1. Execute $ forge from a command prompt.
  2. Create a new project:
    $ new-project --named {name} --topLevelPackage {com.package} --projectFolder {/directory/path}
  3. Install the web-scaffold facet, and press ENTER to confirm installation of required facet dependencies and/or packaging types:
    $ scaffold setup 
  4. That's it! Now in a separate command shell, build your project using Maven, and deploy it onto your JBoss Application Server instance:
    $ mvn clean package
    $ mvn jboss:hard-deploy
    $ mvn jboss:start
  5. Access your application at: http://localhost:8080/{name}-1.0.0-SNAPSHOT/

Part of Forge's architecture is to allow extensions to be created with extreme ease. This is done using the same programming model that you would use for any CDI or Java EE application, and you should quickly recognize the annotation-driven patterns and practices applied.

A Forge plugin could be as simple as a tool to print files to the console, or as complex as deploying an application to a server, 'tweet'ing the status of your latest source-code commit, or even sending commands to a home-automation system; the sky is the limit!

Because Forge is based on Maven, the easiest way to get started quickly writing a plugin is to create a new maven Java project. This can be done by hand, or using Forge's build in plugin project facet.

After following all of the steps in this section, you should now be ready to install your Plugin into the Forge environment. This is accomplished simply by packaging your Plugin in a JAR file with a CDI activator, otherwise referred to as a /META-INF/beans.xml file.

Tip

You must include a /META-INF/beans.xml file in your JAR, or none of the classes in your archive will be discovered; therefore, your Plugin will not be made available to Forge.

Now that you have implemented the Plugin interface, it's time to add some functionality. This is done by adding "Commands" to your plugin class. Commands are plain Java methods in your plugin Class. Plugin methods must be annotated as either a @DefaultCommand, the method to be invoked if the plugin is called by name (with no additional commands), or @Command(name="..."), in which case a the plugin name and command name must both be used to invoke the method.

Commands also accept @Options parameters as arguments. These are described in detail later in this section.

Once we have a command or two in our Plugin, it's time to give our users some control over what it does; to do this, we use @Option params; options enable users to pass information of various types into our commands.

Options can be named, in which case they are set by passing the --name followed immediately by the value, or if the option is a boolean flag, simply passing the flag will signal a `true` value. Named parameters may be passed into a command in any order, while unnamed parameters must be passed into the command in the order with which they were defined.

In addition to --named option parameters, as described above, parameters may also be passed on the command line by the order in which they are entered. These are called "ordered option parameters", and do not require any parameters other than help or description information.

@Option String value

The order of the options in the method signature controls how values are assigned from parsed Forge shell command statements.

For example, the following command accepts several options, named 'one', and 'two':

public class ExamplePlugin implements Plugin {
   @Command(name="perform")
   public void exampleCommand( 
                  @Option String one,
                  @Option String two,
                  PipeOut out) {
       out.println(">> first option equals: " + one);
       out.println(">> second option equals: " + two);
   }
}

The above command, when executed, would produce the following output:

$ example-plugin cat dog 
>> option one equals: cat
>> option two equals: dog

Both --named and ordered option parameters can be mixed in the same command; there are some constraints on how commands must be typed, but there is a great deal of flexibility as well.

@Option String value,
@Option(name="num") int number

The order of ordered options in the method signature controls how values are assigned from the command line shell, whereas the named options have no bearing on the order in which inputs are provided on the command line.

For example, the following command accepts several options, named 'one', 'two', and several more options that are not named:

public class ExamplePlugin implements Plugin {
   @Command(name="perform")
   public void exampleCommand( 
                  @Option(name="one") String one,
                  @Option(name="two") String two,
                  @Option String three,
                  @Option String four,
                  PipeOut out) {
       out.println(">> first option equals: " + one);
       out.println(">> second option equals: " + two);
       out.println(">> third option equals: " + three);
       out.println(">> fourth option equals: " + four);
   }
}

The above command, when executed, would produce the following output:

$ example-plugin --one cat --two dog bird lizard  
>> option one equals: cat
>> option two equals: dog 
>> option one equals: bird
>> option two equals: lizard

However, we could also achieve the same result by re-arranging parameters, and as long as the name-value pairs remain together, and the ordered values are passed in the correct order, interpretation will remain the same:

$ example-plugin --two dog bird --one cat lizard  
>> option one equals: cat
>> option two equals: dog 
>> option one equals: bird
>> option two equals: lizard

This table describes all of the available @Option(...) attributes and their usage.

AttributeTypeDescriptionDefault
nameString If specified, defines the --name with which this option may be passed on the command line. If left blank, this option will be ordered (not named,) and will be passed in the order with which it was written in the Method signature.
@Option(name="exampleName")
none
requiredboolean Options may be declared as required when they must be supplied in order for proper command execution. The shell will enforce this requirement by prompting the user for valid input if the option is omitted when the command is executed.
@Option(required="false")
false
shortNameString If specified, defines the short name by which this option may be called on the command line. Short names must be one character in length. short name with which this option may be passed on the command line. none
    

Much like a standard UNIX-style shell, the Forge shell supports piping IO between executables; however in the case of forge, piping actually occurs between plugins, commands, for example:

$ cat /home/username/.forge/config | grep automatic 
@/* Automatically generated config file */;

This might look like a typical BASH command, but if you run forge and try it, you may be surprised to find that the results are the same as on your system command prompt, and in this example, we are demonstrating the pipe: '|'

In order to enable piping in your plugins, you must use one or both of the @PipeIn InputStream stream or PipeOut out command arguments. Notice that PipeOut is a java type that must be used as a Method parameter, whereas @PipeIn is an annotation that must be placed on a Java InputStream Method parameter.

`PipeOut out` - by default - is used to print output to the shell console; however, if the plugin on the left-hand-side is piped to a secondary plugin on the command line, the output will be written to the `@PipeIn InputStream stream` of the plugin on the right-hand-side:

$ left | right

Or in terms of pipes, this could be thought of as a flow of data from left to right:

$ PipeOut out -> @PipeIn InputStream stream

Notice that you can pipe output between any number of plugins as long as each uses both a @PipeIn InputStream and PipeOut:

$ first command | second command | third command

Take the 'grep' command itself, for example, which supports two methods of invocation: Invocation on one or more Resource<?> objects, or invocation on a piped InputStream.

@Alias("grep")
@Topic("File & Resources")
@Help("print lines matching a pattern")
public class GrepPlugin implements Plugin
{
   @DefaultCommand
   public void run(
         @PipeIn final InputStream pipeIn,
         @Option(name = "ignore-case", shortName = "i", flagOnly = true) boolean ignoreCase,
         @Option(name = "regexp", shortName = "e") String regExp,
         @Option(description = "PATTERN") String pattern,
         @Option(description = "FILE ...") Resource<?>[] resources, 
         final PipeOut pipeOut
   ) throws IOException
   {
      Pattern matchPattern = /* determine pattern (omitted for space) */;

      if (resources != null) {
      
         /* User passed file(s) on the command line; grep those. */
      
         for (Resource<?> r : resources) {
            InputStream inputStream = r.getResourceInputStream();
            try {
               match(inputStream, matchPattern, pipeOut, ignoreCase);
            }
            finally {
               inputStream.close();
            }
         }
      }
      else if (pipeIn != null) {
      
         /* No files were passed on the command line; check for a 
          * piped InputStream and use that.
          */
      
         match(pipeIn, matchPattern, pipeOut, ignoreCase);
      }
      else {
      
         /* No input was passed to the plugin. */
      
         throw new RuntimeException("Error: arguments required");
      }
   }

   private void match(InputStream instream, Pattern pattern, PipeOut pipeOut, boolean caseInsensitive) throws IOException {
      StringAppender buf = new StringAppender();

      int c;
      while ((c = instream.read()) != -1) { /* Read from the given stream. */
         switch (c) {
         case '\r':
         case '\n':
            String s = caseInsensitive ? buf.toString().toLowerCase() : buf.toString();

            if (pattern.matcher(s).matches()) {
               pipeOut.println(s); /* Write to the output pipe. */
            }
            buf.reset();
            break;
         default:
            buf.append((char) c);
            break;
         }
      }
   }
}

This reference guide was written for Forge: a tool from JBoss by Red Hat, Inc.