Skip navigation

Introduction

In the first part of this two part blog, you were introduced to Fpak (Forge Package), the templating feature of Forge.  I covered how to download/compile/install Fpak, described the layout and structure of the .fpk file, and covered the use of the Runner to allow execution of a .fpk file outside of Forge.  In this second part, I attempt to convert the jboss-javaee6-webapps Maven Archetype to a .fpk file.  In this process, I will cover:

 

  • Generating a .fpk file with an existing project layout using the default Fpak generator
  • Wrapping a .fpk file with a plugin to allow it to be registered in Forge
  • Installing the wrapped .fpk file into Forge
  • Running the new plugin from Forge

 

Once these topics are covered, I will list some ideas for the improvement of the Fpak functionality.  There's a lot to cover, so let's get started.

 

Generating a .fpk file

Having a templating feature available to Forge begs the question, "How can I create the needed template?".  Well, the wizard developers would perhaps prefer to code them from scratch using vi (or emacs to prevent *that* holy war), but for the rest of us mere mortals we may want something that makes it easier for us.  So, Fpak has the ability to inspect an existing project layout and convert it to the necessary format for the file creation section of the .fpk file.  This would allow a developer to work with the project to get it right before codifying it in the .fpk file.  At this point there are a couple of caveats to the default generator provided by Fpak that should be noted.  First, Fpak was created as a templating engine to augment Forge and as such, works solely on text based files.  This is important because when you are trying to generate a .fpk file from an existing project, the binary resources of the project will *not* be accommodated.  One work around could be to extract binary files into a "binary resource" zip that can be overlaid onto a generated project.  Second, once a project is laid out and ready to be templatized, the tokens to be replaced will need to be manually modified in the .fpk file (e.g. class names or packaging names).

 

Generating from jboss-javaee6-webapps

Now that you've been introduced to the default generator, let's take a look at how it works in action.  For this, I will first run the jboss-javaee6-webapps archetype to generate a project layout that will become input into the default generator.  Here are the steps I used in accomplishing this:

 

  • create a directory to hold the generated project in
  • cd to the directory
  • execute the  mvn archetype command below using the values shown

 

$ mvn archetype:generate -DarchetypeArtifactId=jboss-javaee6-webapp -DarchetypeGroupId=org.jboss.weld.archetypes -DarchetypeRepository=central
Define value for property 'groupId': : GROUPIDREPLACE
Define value for property 'artifactId': : ARTIFACTIDREPLACE
Define value for property 'version':  1.0-SNAPSHOT: : 
Define value for property 'package':  GROUPIDREPLACE: : org.replace.pkg
[INFO] Using property: name = Java EE 6 webapp project
Confirm properties configuration:
groupId: GROUPIDREPLACE
artifactId: ARTIFACTIDREPLACE
version: 1.0-SNAPSHOT
package: org.replace.pkg
name: Java EE 6 webapp project
 Y: : 

 

I've used values that will be easy to replace in the .fpk file with the appropriate variable (token) syntax.  You can compile the generated project to ensure that it is valid and will build, but if you do, be sure to remove the target directory before running the generator on the project layout.  Now the default generator can be run from the project root directory.  The generator is in the jar generated in part 1 of the blog.  It can be run by executing the following command:

 

$ java -cp <fpak_directory>/target/fpak-1.0-SNAPSHOT.jar org.jboss.fpak.generator.FPakGenerator <generated_file_name>.fpk

 

This will generate the .fpk file in the project root directory by default.  I ran the default generator on the jboss-javaee6-webapp project generated in the previous step with a filename of g6-webapp.fpk (g6 inspired by a tweet from Dan Allen .  Remember that the generator only builds the file creation part of the Fpak file.  Now that you have that, there are a few more things to add:

 

  • @inputs section for grabbing any necessary input
  • @init section for any execution needed before processing the templates in the file creation section
  • replace the unique names generated by the archetype with the appropriate variables from either the @inputs or @init section
  • prepend the variable representing the base directory to all filenames

 

Here's what the @inputs and @init sections look like after being added:

 

@inputs:{
    String project : "Name of the project",
    String package : "Fully qualified package name",
    boolean help : "Prints help"
}

@init:{
    if (help) {
        System.out.println("--project <projectName> --package <packageName>");
    }

    if (project == null) {
        fail("You must specify a project name");
        return;
    }


    if (package == null) {
        fail("You must specify a package name");
        return;
    }


    var projectName = project;
    var projectClassName = projectName.substring(0, 1).toUpperCase() + projectName.substring(1);
    var packageName = package;
    var baseDirectory = projectName.toLowerCase();
    var sourceDirectory = packageName.replaceAll('\\\\.', '/');
}

 

Now that the needed variables are defined, GROUPIDREPLACE, ARTIFACTIDREPLACE, and org.replace.pkg can be replaced with @{packageName}, @{projectClassName}, and @{packageName} respectively.  The last step is prepending @{baseDirectory}/ to all the filenames in the file generation section (e.g. ++pom.xml: becomes ++@{baseDirectory}/pom.xml:).  The g6-webapp.fpk file is now complete and ready to be tested with the Runner.  Recall from part 1 of this blog that the Runner provides the ability to process a .fpk file without needed to bootstrap Forge.  The project can be (re)generated with Runner and then built and run to ensure it works.

 

Creating, loading, and running the Forge plugin wrapper

Forge currently doesn't recognize a .fpk file as a valid plugin type which theoretically it doesn't need to.  The gen command is built into Forge and is the mechanism Forge has for processing a .fpk file.  But, the gen command needs a way to discover the available .fpk files to be processed.  This is where the wrapper plugin comes into play.  The wrapper plugin basically allows the .fpk file to become available via the classloader and advertise the .fpk file to the gen command by a name.  If this seems a bit hard to understand, perhaps this piece of code can help illustrate:

 

@ApplicationScoped
public class G6ScaffoldPlugin implements Plugin {
    @Inject
    private Event<AdvertiseGenProfile> event;

    public void init(@Observes PostStartup postStartup) {
        URL g6_fpk = getClass().getClassLoader()
                .getResource("org/jboss/forge/templates/g6-webapp.fpk");

        event.fire(new AdvertiseGenProfile("g6-webapp", g6_fpk));
    }
}

 

Here you can see that the path to the g6-webapp.fpk file is captured as a URL.  That URL in turn is registered with the gen command with the key of g6-webapp via an AdvertiseGenProfile event being fired.  Now all that is needed is a jar file that contains both this wrapper class and the .fpk file.  An example project and pom.xml file can be found at [1].  Once you have a jar file, the jar file can be loaded into Forge using the forge jar-plugin command.  Here's an example:

 

[no project] bin $ forge jar-plugin --jar ~/work/forge-g6-webapp/target/forge-g6-webapp-1.0-SNAPSHOT.jar
 ? [id=plugin identifier, [e.g. "com.example.group : example-plugin"] (of type org.jboss.forge.project.dependencies.Dependency)]: id=g6-webapp
***INFO*** WARNING!
 ? Installing plugins from remote sources is dangerous, and can leave untracked plugins. Continue? [Y/n] 
Wrote /Users/rruss/.forge/plugins/id=g6-webapp$null$1$.jar
***SUCCESS*** Installed from [forge-g6-webapp-1.0-SNAPSHOT.jar] successfully.

 

Now g6-webapp is available to the gen command and can be run from Forge.  Here is an example of that:

 

[no project] work $ gen g6-webapp --help
--project <projectName> --package <packageName>
You must specify a project name
[no project] work $ gen g6-webapp --project blog-g6 --package org.blog.g6
created: /Users/rruss/work/blog-g6/pom.xml
created: /Users/rruss/work/blog-g6/readme.html
created: /Users/rruss/work/blog-g6/readme.txt
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/controller/MemberRegistration.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/data/MemberListProducer.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/data/MemberRepository.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/data/MemberRepositoryProducer.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/data/SeedDataImporter.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/model/Member.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/rest/JaxRsActivator.java
created: /Users/rruss/work/blog-g6/src/main/java/org/blog/g6/rest/MemberResourceRESTService.java
created: /Users/rruss/work/blog-g6/src/main/resources/import.sql
created: /Users/rruss/work/blog-g6/src/main/resources/META-INF/persistence.xml
created: /Users/rruss/work/blog-g6/src/main/resources-jbossas/default-ds.xml
created: /Users/rruss/work/blog-g6/src/main/webapp/index.jsf
created: /Users/rruss/work/blog-g6/src/main/webapp/index.xhtml
created: /Users/rruss/work/blog-g6/src/main/webapp/META-INF/context.xml
created: /Users/rruss/work/blog-g6/src/main/webapp/META-INF/MANIFEST.MF
created: /Users/rruss/work/blog-g6/src/main/webapp/resources/css/screen.css
created: /Users/rruss/work/blog-g6/src/main/webapp/resources/gfx/banner.png
created: /Users/rruss/work/blog-g6/src/main/webapp/resources/gfx/weld.png
created: /Users/rruss/work/blog-g6/src/main/webapp/WEB-INF/beans.xml
created: /Users/rruss/work/blog-g6/src/main/webapp/WEB-INF/faces-config.xml
created: /Users/rruss/work/blog-g6/src/main/webapp/WEB-INF/templates/default.xhtml
created: /Users/rruss/work/blog-g6/src/main/webapp/WEB-INF/web.xml
created: /Users/rruss/work/blog-g6/src/test/java/org/blog/g6/test/MavenArtifactResolver.java
created: /Users/rruss/work/blog-g6/src/test/java/org/blog/g6/test/MemberRegistrationTest.java
created: /Users/rruss/work/blog-g6/src/test/resources/arquillian.xml
created: /Users/rruss/work/blog-g6/src/test/resources-glassfish-embedded/sun-resources.xml
created: /Users/rruss/work/blog-g6/src/test/resources-glassfish-embedded/test-persistence.xml
created: /Users/rruss/work/blog-g6/src/test/resources-jbossas/jndi.properties
created: /Users/rruss/work/blog-g6/src/test/resources-jbossas/test-persistence.xml

 

The project can now be built just as if you had generated it from the original Maven Archetype.  Well ... almost.  If you recall from above, there was a very specific limitation listed.  Do you remember what that was?  Fpak was primarily built to only process text based files.  For anyone who has experience with the jboss-javaee6-webapp archetype, there are image (non-text) files in the project.  Those, and any other non text file, could not be accommodated by the default generator.  After you build the project generated from this plugin you will notice that when the webapp is deployed, you will see broken images.  "But wait, Rodney" you say to yourself, I see some .png files listed above as having been created.  Yup, the default generator tried to capture those files in the .fpk file, and the gen command in Forge tried to process them.  Unfortunately, that transition from being "converted" from binary to text back to binary did not quite preserve the files.

 

Wrap up

Before we take a look at what has been accomplished, I would like to state one more caveat.  While there can be some benefits to converting a Maven Archetype to an Fpak template, that wasn't necessarily what was being demonstrated here.  I used the conversion context to demonstrate the ability to generate a .fpk file from an existing project layout.  Alright, with that out of the way, let's take a look at what was demonstrated here:

 

  • You were introduced to the features and limitations of using the default generator from Fpak to help create a .fpk file from an existing project layout.  The existing project used here was a project that was generated from the jboss-javaee6-webapp Maven Archetype.
  • Once the .fpk file was completed, you were shown how to create a wrapper plugin to help register the created .fpk file with Forge.
  • The wrapper plugin class and the .fpk file were put into a jar file and loaded into Forge.  Once available, the gen command could process the .fpk file from within Forge.  An example of this was shown

 

Between both parts 1 and 2, I hope you were given enough information to understand how template support in Forge is accomplished via Fpak.

 

Opportunity for enhancement

I don't want to completely wrap up without acknowledging some opportunities for enhancements to Fpak's functionality.  Many of these ideas were borne out of writing this blog and identifying some gaps.  Here they are in no particular order of importance:

 

  • support for the conditional addition of a file with a suggested format of ?(conditional_expression)++filename:
  • auto-registration of a .fpk file with Forge to eliminate the need for a wrapper plugin
  • an intermediate step may be the auto-creation of a wrapper plugin, given a .fpk file
  • wire @inputs with Forge's CLI processing to provide <TAB> completion and heuristic argument discovery via <TAB>
  • make tokenizing values in a file easier from the generator
  • use Forge to actually generate the .fpk file and provide the opportunity to use commands to generate the @inputs and @init section
  • find a cleaner way to deal with binary resources of a project
  • .fpakignore file as input to the generator, similar in functionality to .gitignore

 

These are just a few of my ideas for improving Fpak.  What are you ideas for making this better/easier to use?

 

[1]  https://github.com/rdruss/forge-g6-webapp

Introduction

Seam Forge is a tool that looks to lower the barrier to entry for starting projects, automate repetitive tasks (e.g. boilerplate project setup), and provide incremental technology additions to your project.  The latest release is Alpha 3 and you can read what Lincoln has to say about it here:

 

http://community.jboss.org/people/lincolnthree/blog/2011/04/04/seam-forge-100alpha3-angry-kittens-released

 

While there has been quite a bit of interest in using and creating plugins for Forge, (e.g. Paul Bakker's Arquillian plugin), there have also been questions like "Where's the templating support?" (Rob Williams) or "Isn't Maven Archetype good enough?".  In answering the second question, the current version of Archetype makes it difficult to make choices and once you've run the archetype, you're done (i.e. you cannot further enhance your project).  The answer to the first question is the subject of this first of a two part blog.  Fpak (Forge Package) is a plugin created by Mike Brock that provides a framework for processing templates to be used with Forge.  In part 1, we'll look at what Fpak is and what it does.  In part 2, I'll try to re-create the jboss-javaee6-webapp archetype structure with Fpak.  (Note: The default templating support is based on MVEL 2, but anyone can add support for their favorite templating engine. Taking a deeper look at how to do this could be the subject of a subsequent blog post if there's interest.)

 

Getting Fpak

To start, let's take a look at how you can currently get a hold of Fpak.  Since there hasn't been a release yet of Fpak, you'll have to build it yourself.  Have no fear, it's a fairly painless process.

 

  • prerequisites are git (I have 1.7.2), Maven (I have 3.0.3), and Java 6
  • cd into the directory where you will "install" the source code
  • clone the Fpak repository with: git clone https://github.com/mikebrock/fpak.git
  • cd int the fpak directory
  • run: mvn clean package
  • in the target directory you will find fpak-1.0-SNAPSHOT.jar
    • this jar has all the needed dependencies in it given it used shade.  Note: there is a debate going on in Seam Forge about how to best handle dependencies that you can follow on the forge-dev mailing list if you're interested.

 

First look

Now that you have Fpak downloaded and built, let's take a look at the structure of a .fpk file.  A test file can be found in the src/test/resources directory called test1.fpk.  The file looks like this:

 

@inputs:{
    String name : "Fully qualified class name",
    boolean help : "Prints help"
}

@init:{
  if (help) {
      System.out.println(" --name <fullyQulifiedClassName>");
  }

  if (name == null) {
     fail("You must specify a class name");
     return;
  }

  package = name.substring(0, name.lastIndexOf('.'));
  packageDir = package.replaceAll('\\\\.', '/');
  classname = name.substring(package.length() + 1);
}

++@{packageDir}/@{classname}.java:{
package @{package};

public class @{classname} {

}
}

++@{packageDir}/@{classname}Entity.java:{
package @{package};

public class @{classname}Entity {

}
}

 

There are three primary sections to this file: 2 named blocks (@inputs and @init) and the file creation section.  The @inputs block is where your inputs can be defined.  You define the type and name along with an optional description.  The format looks like this:

 

(String|boolean) <input_name> : "<short description here>"

 

In processing the inputs, a String will pull what comes after the input and place it into a variable that matches the input name.  For example, when test1.fpk is processed with --name foobar, then value foobar gets placed into the name variable.  booleans are just tested for there presence.  Again, in the test1.fpk example, if it is processed with --help, then a variable called help will be set to true.

 

The next named section is the @init section.  This section is processed after the @inputs section but before the file creation section.  Given that the default templating language is MVEL 2, you can place any valid MVEL 2 syntax into this section.  This includes conditionals and variable definitions.  Taking a look at test1.fpk, you can see that the variables defined in the @inputs section are available to the @init section and that variables being defined in the @init section will be referenced in the file creation section.  For more information on what can be defined in the @init section, take a look at the MVEL 2 documentation.

 

Last, but certainly not least is the file creation section.  Each file that will be created is prefaced by a ++ operator followed by the fully qualified file name.  The contents of the file are defined between the :{ and } delimiters.  Remember that variables defined in the @init section can be referenced by these file templates via the @{variable_name} format.  In this simple example, you'll notice that 2 files are created, <Classname>.java and <Classname>Entity.java.

 

Let's try this

Let's run Fpak on test1.fpk to see how this all works.  Now, Fpak was created in order to run within Forge to allow for further Forge commands to be executed on the created project.  But, when you defining a .fpk script, you probably don't want to have to bootstrap Forge to test the script.  That's where the Runner for Fpak comes into play.  Runner allows you to execute the .fpk script and see the results before wrapping it and making it available as a Forge plugin.  So, here is how you can use Runner to run the test1.fpk script:

 

  • create a tmp directory in the fpak root directory
  • cd to tmp
  • run:  java -cp ../target/fpak-1.0-SNAPSHOT.jar org.jboss.fpak.Runner ../src/test/resources/test1.fpk --name org.test.Foo
  • here's a sample of the output:

 

$ java -cp ../target/fpak-1.0-SNAPSHOT.jar org.jboss.fpak.Runner ../src/test/resources/test1.fpkWorking in: /Users/rruss/work/fpak/tmp/.
You must specify a class name

$ java -cp ../target/fpak-1.0-SNAPSHOT.jar org.jboss.fpak.Runner ../src/test/resources/test1.fpk --help
Working in: /Users/rruss/work/fpak/tmp/.
 --name <fullyQulifiedClassName>
You must specify a class name

$ java -cp ../target/fpak-1.0-SNAPSHOT.jar org.jboss.fpak.Runner ../src/test/resources/test1.fpk --name org.test.Foo
Working in: /Users/rruss/work/fpak/tmp/.
created: /Users/rruss/work/fpak/tmp/./org/test/Foo.java
created: /Users/rruss/work/fpak/tmp/./org/test/FooEntity.java

$ find .
.
./org
./org/test
./org/test/Foo.java
./org/test/FooEntity.java

 

And there you have it.  This was a quick introduction to Fpak and what it can do.  In part 2, I'll show my attempt at trying to mimic the jboss-javaee6-webapp Maven Archetype using Fpak.

Filter Blog

By date: By tag: