Saturday, April 30, 2011

Object User Interface Mapper (OIM): Metawidget v1.15

Version 1.15 of Metawidget, the Object User Interface Mapping tool (OIM) is now available! This release includes the following enhancements:
As always, the best place to start is the Reference Documentation:

http://metawidget.org/doc/reference/en/pdf/metawidget.pdf

Special thanks to Renato Garcia and Lincoln Baxter III for their help with this release!

Your continued feedback is invaluable to us. Please download it and let us know what you think.

Thursday, April 28, 2011

Metawidget: RichFaces 4 support

The next release of Metawidget (v1.15) will include support for RichFaces 4 (previously we only supported 3.3.3.Final). Code and unit tests are already available in SVN, and here are some screenshots:

This support requires MyFaces 2.0.5 or above, or a version of Mojarra that includes this fix.

Wednesday, April 20, 2011

Metawidget: Streamlined Configuration

The next release of Metawidget (v1.15) will include a small enhancement to the metawidget.xml syntax used by ConfigReader. ConfigReader has always been designed to automatically recognise and reuse immutable instances. This works both when configuring multiple Metawidgets, and configuring multiple settings for a single Metawidget. So if you did...

<inspectors>
   <array>
      <java5Inspector xmlns="java:org.metawidget.inspector.java5" config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
         <propertyStyle>
            <javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean" config="JavaBeanPropertyStyleConfig">
               <supportPublicFields>
                  <boolean>false</boolean>
               </supportPublicFields>
               <privateFieldConvention>
                  <format>'m'{1}</format>
               </privateFieldConvention>
            </javaBeanPropertyStyle>

         </propertyStyle>                  
      </java5Inspector>
      <metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation" config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
         <propertyStyle>
            <javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean" config="JavaBeanPropertyStyleConfig">
               <supportPublicFields>
                  <boolean>false</boolean>
               </supportPublicFields>
               <privateFieldConvention>
                  <format>'m'{1}</format>
               </privateFieldConvention>
            </javaBeanPropertyStyle>

         </propertyStyle>                  
      </metawidgetAnnotationInspector>
   </array>
</inspectors>

...ConfigReader would be smart enough to reuse the same javaBeanPropertyStyle instance. But you still had to specify all javaBeanPropertyStyle's configuration options twice. In v1.15 you can instead use id and refId to refer from one setting to another:

<inspectors>
   <array>
      <java5Inspector xmlns="java:org.metawidget.inspector.java5" config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
         <propertyStyle>
            <javaBeanPropertyStyle xmlns="java:org.metawidget.inspector.impl.propertystyle.javabean" config="JavaBeanPropertyStyleConfig" id="myPropertyStyle">
               <supportPublicFields>
                  <boolean>false</boolean>
               </supportPublicFields>
               <privateFieldConvention>
                  <format>'m'{1}</format>
               </privateFieldConvention>
            </javaBeanPropertyStyle>
         </propertyStyle>                  
      </java5Inspector>
      <metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation" config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
         <propertyStyle>
            <javaBeanPropertyStyle refId="myPropertyStyle" />
         </propertyStyle>                  
      </metawidgetAnnotationInspector>
   </array>
</inspectors>

It's a small thing, but can shorten your metawidget.xml in a big way.

Feedback welcome!

Monday, April 11, 2011

Metawidget: Maven Snapshot Releases

I'm thrilled to say that the first snapshot releases of Metawidget 1.15 are now available in the JBoss Snapshot Repository at https://repository.jboss.org/nexus/content/repositories/snapshots/org/metawidget:

<repository>
   <id>jboss-snapshots</id>
   <url>https://repository.jboss.org/nexus/content/repositories/snapshots</url>
</repository>
...
<dependency>
   <groupId>org.metawidget.modules</groupId>
   <artifactId>metawidget-all</artifactId>
   <version>1.15-SNAPSHOT</version>
</dependency>

I plan to be staging the modules here for a few weeks before the final 1.15 release. I'd be most grateful to anybody who could download them and take the new fine-grained module system for a spin.

My thanks to Renato Garcia for his help with the Maven migration.

Feedback most welcome!

Friday, April 1, 2011

Grokking Seam Forge: Part 3

JBoss have just released Seam 3.0.0.Final, which includes Alpha 3 of Seam Forge. So I thought I'd improve my previous blog entry:

  • First, download Seam from here
  • Unzip to a folder of your choosing
  • Run bin\forge
Type the following commands and press ENTER after each one. Accept any defaults you are prompted for, and remember to use TAB and UP ARROW autocomplete:

new-project --named MyApp --topLevelPackage com.myapp
persistence setup --provider HIBERNATE --container JBOSS_AS6
new-entity --named Person
new-field string --named firstname
new-field string --named surname
new-field int --named age
new-field string --named notes
new-field custom --named homeAddress
[type=The qualified Class to be used as this field's type (of type java.lang.String)]: com.myapp.Address
new-field custom --named workAddress
[type=The qualified Class to be used as this field's type (of type java.lang.String)]: com.myapp.Address
new-entity --named Address
new-field string --named street
new-field string --named suburb
new-field string --named state
new-field string --named postcode
scaffold from-entity com.myapp.domain.Person.java
exit


Next:

  • Open Eclipse (with m2eclipse installed)

  • Choose File > Import > Existing Maven Projects and import the pom.xml at \seam-3.0.0.Final\forge\MyApp\pom.xml

  • Right click the pom.xml and choose Run As > Maven package

  • Take the WAR it generates under target/MyApp-1.0.0-SNAPSHOT.war and deploy it under JBoss AS 6.0.0.Final
Open your browser at http://localhost:8080/MyApp-1.0.0-SNAPSHOT/scaffold/person/list.jsf:
Now, let's play around a bit! These are things that soon Forge will be able to do for you, but for now edit Person.java and add the lines in bold:

package com.myapp.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Version;

import org.metawidget.inspector.annotation.UiComesAfter;
import org.metawidget.inspector.annotation.UiSection;

@Entity
public class Person
   implements java.io.Serializable {

   @Id
   private @GeneratedValue( strategy = GenerationType.AUTO )
   @Column( name = "id", updatable = false, nullable = false )
   long   id      = 0;

   @Version
   private @Column( name = "version" )
   int      version   = 0;

   public long getId() {

      return this.id;
   }

   public void setId( final long id ) {

      this.id = id;
   }

   public int getVersion() {

      return this.version;
   }

   public void setVersion( final int version ) {

      this.version = version;
   }

   @Column
   private String   firstname;

   public String getFirstname() {

      return this.firstname;
   }

   public void setFirstname( final String firstname ) {

      this.firstname = firstname;
   }

   @Column
   private String   surname;

   @UiComesAfter( "firstname" )
   public String getSurname() {

      return this.surname;
   }

   public void setSurname( final String surname ) {

      this.surname = surname;
   }

   @Column
   private int   age;

   @UiComesAfter( "surname" )
   public int getAge() {

      return this.age;
   }

   public void setAge( final int age ) {

      this.age = age;
   }

   @Column
   private String   notes;

   @UiComesAfter( "workAddress" )
   @Lob
   @UiSection( "Other" )

   public String getNotes() {

      return this.notes;
   }

   public void setNotes( final String notes ) {

      this.notes = notes;
   }

   @Column
   private Address   homeAddress = new Address();

   @UiComesAfter( "age" )
   @UiSection( "Details" )

   public Address getHomeAddress() {

      return this.homeAddress;
   }

   public void setHomeAddress( final Address homeAddress ) {

      this.homeAddress = homeAddress;
   }

   @Column
   private Address   workAddress = new Address();

   @UiComesAfter( "homeAddress" )
   public Address getWorkAddress() {

      return this.workAddress;
   }

   public void setWorkAddress( final Address workAddress ) {

      this.workAddress = workAddress;
   }

   @Override
   public String toString() {

      return this.getClass().getSimpleName() + "[" + id + ", " + version + ", " + firstname + ", " + surname + ", " + age + ", " + notes + ", " + homeAddress + ", " + workAddress + "]";
   }
}

Similarly for Address.java:

package com.myapp.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

import org.metawidget.inspector.annotation.UiComesAfter;

@Entity
public class Address
   implements java.io.Serializable {

   @Id
   private @GeneratedValue( strategy = GenerationType.AUTO )
   @Column( name = "id", updatable = false, nullable = false )
   long   id      = 0;

   @Version
   private @Column( name = "version" )
   int      version   = 0;

   public long getId() {

      return this.id;
   }

   public void setId( final long id ) {

      this.id = id;
   }

   public int getVersion() {

      return this.version;
   }

   public void setVersion( final int version ) {

      this.version = version;
   }

   @Column
   private String   street;

   public String getStreet() {

      return this.street;
   }

   public void setStreet( final String street ) {

      this.street = street;
   }

   @Column
   private String   suburb;

   @UiComesAfter( "street" )
   public String getSuburb() {

      return this.suburb;
   }

   public void setSuburb( final String suburb ) {

      this.suburb = suburb;
   }

   @Column
   private String   state;

   @UiComesAfter( "suburb" )
   public String getState() {

      return this.state;
   }

   public void setState( final String state ) {

      this.state = state;
   }

   @Column
   private String   postcode;

   @UiComesAfter( "state" )
   public String getPostcode() {

      return this.postcode;
   }

   public void setPostcode( final String postcode ) {

      this.postcode = postcode;
   }
}

Then tell metawidget.xml we'd like to use RichFaces:

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
   ...
   <widgetBuilder>   
      <compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite" config="CompositeWidgetBuilderConfig">
         <widgetBuilders>
            <array>
               <overriddenWidgetBuilder xmlns="java:org.metawidget.faces.component.widgetbuilder"/>
               <readOnlyWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
               <richFacesWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder.richfaces"/>
               <htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder" config="HtmlWidgetBuilderConfig"/>
            </array>
         </widgetBuilders>
      </compositeWidgetBuilder>
   </widgetBuilder>

   <layout>
      <tabPanelLayoutDecorator xmlns="java:org.metawidget.faces.component.html.layout.richfaces" config="TabPanelLayoutDecoratorConfig">
         <layout>
            <simpleLayout xmlns="java:org.metawidget.faces.component.layout"/>
         </layout>
      </tabPanelLayoutDecorator>
   </layout>


</htmlMetawidget>

And tell web.xml the same (update: this can be done using the richfaces-forge-plugin, see the comments below):

<web-app >
   ...
   <filter>
      <display-name>RichFaces Filter</display-name>
      <filter-name>richfaces</filter-name>
      <filter-class>org.ajax4jsf.Filter</filter-class>
   </filter>

   <filter-mapping>
      <filter-name>richfaces</filter-name>
      <servlet-name>Faces Servlet</servlet-name>
      <dispatcher>REQUEST</dispatcher>
      <dispatcher>FORWARD</dispatcher>
      <dispatcher>INCLUDE</dispatcher>
   </filter-mapping>

   <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>*.jsf</url-pattern>
   </servlet-mapping>

</web-app>

And finally pom.xml too (update: this can be done using add-dependency, see the comments below):

<dependencies>
   ...
   <dependency>
      <groupId>org.richfaces.ui</groupId>
      <artifactId>richfaces-ui</artifactId>
      <version>3.3.3.Final</version>
      <scope>compile</scope>
   </dependency>
   <dependency>
      <groupId>org.richfaces.framework</groupId>
      <artifactId>richfaces-impl</artifactId>
      <version>3.3.3.Final</version>
      <scope>compile</scope>
   </dependency>

</dependencies>

Then redeploy your app and hit http://localhost:8080/MyApp-1.0.0-SNAPSHOT/scaffold/person/list.jsf again:
Of course, Seam Forge is still in alpha so not everything works yet. But hopefully this has whetted your appetite for things to come!

Friday, March 25, 2011

Metawidget: Full Speed To Maven

The next release of Metawidget (v1.15) represents a significant rearrangement of our source tree, as we migrate the codebase from Ant to Maven.

I'm not going to knock Ant: it's been a great build tool for us, and it's much more flexible than Maven in many regards. But there are some compelling reasons for us to switch:

  • Metawidget wires up existing technologies, and there are lots of them.
    I really like the idea of a single metawidget.jar that's easy to deploy and get running, but there are some use cases where this isn't sufficient. For example, perhaps you want to save space by deploying only those Metawidget plugins you actually use. Or perhaps you need to deploy different plugins on different application tiers.

    We need a path from the easy-to-use but monolithic metawidget.jar to this 'fine-grained' use case. Maven gives us this path. So with v1.15 you'll be able to do either...

    <dependency>
       <groupId>org.metawidget.modules</groupId>
       <artifactId>metawidget-all</artifactId>
    </dependency>

    Or you can specify fine-grained dependencies...

    <dependency>
       <groupId>org.metawidget.modules.faces</groupId>
       <artifactId>metawidget-richfaces</artifactId>
    </dependency>
    <dependency>
       <groupId>org.metawidget.modules</groupId>
       <artifactId>metawidget-jpa</artifactId>
    </dependency>
    <dependency>
       <groupId>org.metawidget.modules</groupId>
       <artifactId>...other modules...</artifactId>
    </dependency>

    Maven will automatically drag in related dependencies for you, such as org.metawidget.modules:metawidget-core.
  • Metawidget needs lots of high quality examples.
    Whether it's examples on how to run Metawidget on Android, or in GWT Hosted Mode, or with JSF 2, or how to write custom Inspectors or WidgetBuilders - high quality examples are critical. Maven lets us compose each small piece of Metawidget (every example, every plugin) as a standalone project. This makes it easy to use tools such as m2eclipse to open the individual pom.xml in your IDE and start experimenting!

  • Some great tools play nicer with Maven.
    Tools such as Jenkins and Sonar, which should have a direct bearing on maintaining our code quality.
Don't Panic!

To ally any concerns, allow me to stress:

  • Metawidget v1.15 will be backwards compatible with v1.10.
    In fact, the binaries will be almost identical. Only the build system has been rewritten.
  • Metawidget will not require Maven to use.
    We will still be packaging metawidget-1.15.zip and metawidget-1.15-src.zip distributions that you can download, just as before.
All up, I don't think anything will be lost (apart from sweat and tears rearranging the code) and hopefully a lot will be gained. I've branched the old v1.10 codebase in SVN and checked in an early copy of the Maven version. I'd say it's about 75% done (and has nearly 100 POMs!)

I'd be most grateful to anybody who could check it out and provide feedback.

Wednesday, March 9, 2011

Grokking Seam Forge: Part 2

JBoss have just released Seam 3.0.0.CR2, which includes Alpha 2 of Seam Forge. It's an alpha, so expect pain, but let's take it for a spin!

  • First, download Seam Forge from here
  • Unzip to a folder of your choosing
  • Run bin\forge
Seam Forge is command-line driven, similar to Rails and Roo (though with loftier goals :). Type the sections in bold:

[no project] forge-1.0.0.Alpha2 $ new-project --named MyApp --topLevelPackage com.myapp
Use [/forge-1.0.0.Alpha2/MyApp] as project directory? [Y/n] y
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/resources/META-INF/forge.xml
***SUCCESS*** Created project [MyApp] in new working directory [/forge-1.0.0.Alpha2/MyApp]

[MyApp] MyApp $ persistence setup --provider HIBERNATE --container JBOSS_6
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/resources/META-INF/persistence.xml
***SUCCESS*** Installed [forge.spec.jpa] successfully.
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/resources/META-INF/persistence.xml

[MyApp] MyApp $ new-entity --named Person
In which package you'd like to create this @Entity, or enter for default: [com.myapp.domain]
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/java/com/myapp/domain/Person.java
Created @Entity [com.myapp.domain.Person]
Picked up type : com.myapp.domain.Person

[MyApp] Person.java $ new-field string --fieldName firstName
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/java/com/myapp/domain/Person.java
Added field to com.myapp.domain.Person: @Column private String firstName;
[MyApp] Person.java $ new-field string --fieldName surname
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/java/com/myapp/domain/Person.java
Added field to com.myapp.domain.Person: @Column private String surname;
[MyApp] Person.java $ cd ..
[MyApp] domain $ scaffold from-entity com.myapp.domain.Person.java
The [forge.maven.WebResourceFacet] facet requires the following packaging type [war], but is currently [jar], would you like to change the packaging to [war]? (Note: this could break other plugins in your project.) [Y/n] y
Packaging updated to [war]
***SUCCESS*** Installed [forge.maven.WebResourceFacet] successfully.
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/WEB-INF/beans.xml
***SUCCESS*** Installed [forge.spec.cdi] successfully.
The [forge.spec.jsf] facet depends on the following missing facets: [forge.spec.servlet]. Would you like to attempt installation of these facets as well? [Y/n] y
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/WEB-INF/web.xml
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/index.html
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/WEB-INF/faces-config.xml
***SUCCESS*** Installed [forge.spec.jsf] successfully.
***SUCCESS*** Scaffolding installed.
No scaffold type was provided, use Forge default? [Y/n] y
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/WEB-INF/beans.xml
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/java/org/jboss/seam/forge/persistence/DatasourceProducer.java
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/java/org/jboss/seam/forge/persistence/PersistenceUtil.java
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/resources/forge-template.xhtml
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/resources/forge.css
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/resources/favicon.ico
***INFO*** [/forge-1.0.0.Alpha2/MyApp/src/main/java/com/myapp/domain/Person.java] File exists, re-run with `--overwrite` to replace existing files.
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/java/com/myapp/view/PersonBean.java
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/scaffold/person/view.xhtml
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/scaffold/person/create.xhtml
Wrote /forge-1.0.0.Alpha2/MyApp/src/main/webapp/scaffold/person/list.xhtml
***SUCCESS*** Generated UI for [com.myapp.domain.Person]

Next:

  • Open Eclipse (with m2eclipse installed)

  • Choose File > Import > Existing Maven Projects and import the pom.xml at \forge-1.0.0.Alpha2\MyApp\pom.xml
  • Edit src/main/resources/META-INF/persistence.xml and remove the line that says <non-jta-data-source/>
  • (a bug)
  • Right click the pom.xml and choose Run As > Maven package

  • Take the WAR it generates under target/MyApp-1.0.0-SNAPSHOT.war and deploy it under JBoss AS 6.0.0.Final

  • Open your browser at http://localhost:8080/MyApp-1.0.0-SNAPSHOT/scaffold/person/list.jsf:
  • Click Create:
And that's it! Seam Forge has created a full web application for you, complete with JPA back-end and JSF front-end, without you having to write a single line of code. Of course there's much more to it than that, but hopefully this gives you a taste for what JBoss and the Seam Team are cooking up!