Friday, August 13, 2010

Best Practice (my way) for managing "states" in ItemRenderer

When you have an ItemRenderer that has many states and the look/size of the ItemRenderer changes drastically between these states, what's the best way to manage them? What do you do when you need to maintain/keep track of the state of the ItemRenderer?

Because Flex framework reuses ItemRenderer for performance optimization, you can not maintain/keep track of the state in the ItemRenderer itself - ItemRenderer has to be stateless. The state of the ItemRenderer should be maintained by 'data' that drives it. The 'data' should keep enough information about the state of the ItemRenderer such that when ItemRenderer renders this 'data' it can determine which state it should go into.

Here an example -
ItemRendererA has two states 'a' and 'b'. The 'data' it renders is 'objectA'. 'objectA' has two vars - 'stateA' and 'stateB' that keeps track of this particular ItemRenererA state. Note: 'objectA' is part of the model and it persists state information.


<s:States>
  <s:state name='a'/>
  <s:state name='b'/>
</s:States>

// define boolean vars that will drive state changes
// State 'a'
private var _a:Boolean = false;
public function get a():Boolean
{
  return _a;
}
public function set a(value:Boolean):void
{
  if (value)
  {
    _a = value;
    _b = !value;

    // need to do this below to force a call to getCurrentRendererState()
    // ItemRenderer does not have 'invalidateRendererState()' function
    // which really sucks!
    setCurrentState(getCurrentRendererState(), true);
    if (autoDrawBackground)
    {
      redrawRequested = true;
      invalidateDisplayList();
    }
  }
}

// Do the same thing as above for state 'b'

// override set data function to set initial state
override public function set data(value:Object):void
{
  super.data = value;
  initializeRendererState();
}

private function initializeRendererState():void
{
  // 'data' here is 'objectA' which contains vars 'stateA' and 'stateB'
  if (data.stateA)
  {
    // setter for 'a' will force a call to getCurrentRendererState()
    a = true;
  }
  else if (data.stateB)
  {
    // setter for 'b' will force a call to getCurrentRendererState()
    b = true;
  }
}

// returns current state of the ItemRenderer
override protected function getCurrentRendererState():String
{
  if (a)
  {
    return "a";
  }
  else if (b)
  {
    return "b";
  }
}

Code above will make sure that ItemRenderer state is driven off of the 'data' that it's rendering and that ItemRenderer itself remain stateless.

Flex Pretty Formatting

When you have multiple Flex developers working together, it's often a good idea to agree upon a single syntax for .as and .mxml files. It'll enhance readability and maintainability of the code. But it'd be a nightmare trying to adhere to the agreed upon syntax manually so there's a nifty tool (Flash Builder plug-in) that you can use to do so.

Plug-In Site -
http://flexformatter.googlecode.com/svn/trunk/FlexFormatter/FlexPrettyPrintCommandUpdateSite/

Just install this plug-in and you'll have an option called "FlexFormatting" under the Flash Builder preferences. Set the syntax rule for Actionscript and MXML and export it. Now you can give this exported file to your co-developers for one uniform syntax.

Word of Caution - We found out the hard way that if you format existing MXML code using this plug-in, the layout defined in the MXML gets messed(?) up. It's only the syntax/format that gets changed/re-arranged and property values stay absolutely the same, but layout is affected somehow. So be careful to turn on auto formatting feature if you're editing existing MXML files. Make sure the layout stays the same before committing your changes.

Thursday, August 12, 2010

Hudson: Deploy to container based on a schedule

Challenges:
1) The schedules in Hudson jobs apply to Build Triggers. There isn't any separate place to specify a schedule for Post Build Actions like Deploy war/ear to a container.
2) A job without any build information and only deployment info, will not run the Post Build Actions.
3) Cloning an existing job only for the sake of changing the schedule for deployment will mean throwing off the Hudson build numbers placed inside of your artifacts. Even if you un-check the Archive the artifacts option in the cloned job's configuration it will be meaningless ... the clone's artifacts will continue to be published/installed into your local repository over the original ones by maven itself.
4) The clone might get associated as a downstream job and therefore would end up running whenever your original job had finished running. This would make the custom schedule pointless!

Solutions:
  1. Clone the job for which you want to schedule deployments.
  2. If you are using a pom.xml which is set up to use child modules then just directly have the cloned configuration reference the pom.xml of the child which actually puts together the war/ear artifact ... instead of the main parent project's pom.xml file.
  3. Uncheck the following option from the clone's configuration to decouple the upstream/downstream relationship if there is any:
    Build whenever a SNAPSHOT dependency is built
  4. Within the pom.xml file which actually generates your war/ear artifact, place the cargo plugin and its configuration to deploy remotely.
    1. It wouldn't hurt to start with the original documentation for reference on how to do this exactly. But it doesn't talk about the most crucial part which is the use of the cargo.tomcat.manager.url property which is covered in this blog. It is a very good example on how to do this.
    2. Since you are going to commit the altered pom.xml file you may be worried about the side-effects on your original job that performs the build. Well if you build only runs up to the install phase, you don't have to worry about anything as cargo goals are not part of the maven lifecycle up until that point. Everything about your original build will continue to work as it did.
    3. You can also check This build is parameterized and specify string parameters so that you don't have to go changing your pom.xml everytime that your source location of the war/ear file changes or the tomcat that you want to deploy to changes. Here's a sample (+/-)
        <properties>
          <artifactFileLocation>${ARTIFACT_FILE_LOCATION}</artifactFileLocation>
          <remoteTomcat>${REMOTE_TOMCAT_URL}</remoteTomcat>
        </properties>
        ...
            <plugin>
              <groupId>org.codehaus.cargo</groupId>
              <artifactId>cargo-maven2-plugin</artifactId>
              <configuration>
                <!-- Container configuration -->
                <container>
                  <containerId>tomcat6x</containerId>
                  <type>remote</type>
                </container>
                <!-- Configuration to use with the container -->
                <configuration>
                  <type>runtime</type>
                  <properties>
                    <cargo.tomcat.manager.url>${remoteTomcat}/manager</cargo.tomcat.manager.url>
                    <cargo.remote.username>username</cargo.remote.username>
                    <cargo.remote.password>password</cargo.remote.password>
                  </properties>
                </configuration>
                <!-- Deployer configuration -->
                <deployer>
                  <type>remote</type>
                  <deployables>
                    <deployable>
                      <location>${artifactFileLocation}</location>
                      <pingURL>${remoteTomcat}</pingURL>
                    </deployable>
                  </deployables>
                </deployer>
              </configuration>
            </plugin>
    4. Come to think of it, username and password would also benefit from being parameterized.
  5. Take advantage of the new configuration in your artifact generating pom.xml file by configuring the following goal in Hudson for the cloned job:
    org.codehaus.cargo:cargo-maven2-plugin:deployer-redeploy

Wednesday, August 4, 2010

Illustrator to Catalyst: Make Rects not Paths

Catalyst has this annoying habit of converting almost all shapes coming from an Illustrator's .ai file into paths. Even if we have simple rectangles or curved edges, the whole thing becomes a path which is such a waste.

Monday, August 2, 2010

Making the most of Hudson: File System SCM plugin

Often enough your development team may get into situations where they are sure that the code works just fine on their local machines but they are not so sure what would happen if they check-in and let Hudson build it.

Simply downloading & deploying hudson.war to the developer's machine to perform builds may not seem like a very useful solution. The build may now run through the Hudson hosted on the local dev box, but the build configuration would still have to point to an actual repository. This would make the whole endeavor pointless!

This is where the File System SCM comes in. If this plugin is also installed on the Hudson instance deployed to the dev box, then the developer can point to the locally checked out code and modified files as a standalone repository of sorts. It is an easy way to test that Hudson builds go through successfully. If this works then checking into the actual repository after a successful run is not a risk anymore.

Versioning Flex Applications: Displaying Hudson, Maven, SVN build or revision numbers

Hudson sets the values for the following environment variables:
  1. BUILD_NUMBER
  2. SVN_REVISION
In order to show the build and revision numbers in the UI, these variables could be written out to a properties file via a maven project's pom.xml file using a plugin. And then if one did this early enough in the maven lifecycle, your flex application could be compiled to read the version information as key-value pairs from the properties file and show the info on screen when it runs.

Alas there is no decent maven plugin out there to write environment variables out to a file. I found the following plugins out there but none of them (+/-) did the trick.
  1. org.codehaus.mojo:properties-maven-plugin can be found in the following repo You can find proper documentation here. It never printed anything but the Java system level variables so it was useless when it came to tracking the environment variables set by Hudson.
  2. org.sonatype.plugins:maven-properties-plugin can be found in the following repo but this isn't the one you want to use. It has only one goal called filter-file and I cannot find any page documenting its usage no matter how hard I try.
  3. Even placing the info generated by the mvn help:system command-line invocation seemed like a decent idea. But after configuring the same command to be invoked inside a pom file(+/-) I realized that it spit out some header lines that weren't commented out in a manner suited for a properties file.
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-help-plugin</artifactId>
            <version>2.1.1</version>
            <executions>
              <execution>
                <phase>generate-resources</phase>
                <goals>
                  <goal>system</goal>
                </goals>
                <configuration>
                  <output>${basedir}/src/locale/app.properties</output>
                </configuration>
              </execution>
            </executions>
          </plugin>

You can resort to usind the ant plugin inside Maven to do a macro-style replace technique. You can read more about it here.

Or the best way is to have a template file (+-) and leverage Maven model like this (+-)
<?xml version="1.0" encoding="UTF-8"?>
<root>
<buildNumber>${buildNumber}</buildNumber>
<svnRevisionNumber>${svnRevision}</svnRevisionNumber>
</root>
<project >
  ...
  <!-- Map the values provided by the Hudson build to local variables -->
  <properties>
    <buildNumber>${BUILD_NUMBER}</buildNumber>
    <svnRevision>${SVN_REVISION}</svnRevision>
  </properties>
  ...
  <build>
    ...
    <!-- Update version.xml file with the versioning data -->
    <resources>
      <resource>
        <targetPath>${project.build.directory}/${project.artifactId}-${project.version}</targetPath>
        <filtering>true</filtering>
        <directory>${basedir}/src/main/resources</directory>
        <includes><include>version.xml</include></includes>
      </resource>
    </resources>
    ...
  <build>
  ...
<project >

Also as a sidenote: The Hudson variables can be placed inside the manifest file if your artifact being built happens to be a war or jar file. Read more on that here.

Links:
Home / Using Flex 4 / Developer tools / Flex compilers / Using mxmlc, the application compiler / Passing Strings
How to configure flexmojos to pass in compile time variables:
gmail thread
official docs