MileTrack GPS is pain-free business mileage tracking. The gist is that you track your mileage driven using a smartphone or GPS device, upload to the service, and later generate a mileage expense reimbursement report. If you’re interested, we offer a 60-day free trial to test out the system. Head on over to miletrackgps.com to check it out. I’ll also be blogging over there a bit more in the future, so make sure to add that blog to your RSS Feed. We’re on twitter as well @MileTrackGPS

I’ve been working on this project in some way/shape/form for a year and a half now. It started as an electronics project where I was just working on a hardware GPS device. It later morphed into a software project as well once I realized that I couldn’t just sell a piece of hardware alone without competing against really cheap knockoffs manufactured in China.

So, miletrackgps.com was born to solve a pain I had in my life. I wrote a bit about solving this pain with a process over on ourstartupstory.com.

The web front-end is designed of course, with Flex 4.5. The backend is using Java, Spring, GraniteDS, Hibernate, Amazon RDS, all running on the Amazon Elastic Beanstalk. Everything is built with Flex-mojos and maven.

We just released our FREE Android App for tracking mileage to the Google Market. This was developed using Titanium Appcelerator. I blogged a bit about my experience over at riarockstars.com about why I chose that platform for mobile instead of Adobe AIR. Even if you have no real business need for tracking mileage, please give our service a try for the fun of it and send feedback to help us improve the service.

Post to Twitter

Posted by Andrew, filed under Android, Flex, GPS. Date: June 8, 2011, 7:00 am | No Comments »

Flex-Mojos is a set of Maven plugins that allow you to do command-line builds for Flex projects. The real power comes from being able to leverage the power of Maven to do your entire build (front-end, back-end, unit testing, and integration testing) one fell swoop. It’s also very useful if you have a large complex project, or many developers on your team, where using a continuous integration tool such as Hudson would be useful.

Maven is an Apache open-source project that originated in the Java world at Sonatype. If you develop the back-end for your Flex projects in Java, you may already be familiar with Maven.

Adobe currently supports building from the command-line using Apache Ant. While this approach works for smaller projects, it’s difficult to work with large complex projects. Ant takes a very tactical approach to building a project. You give it lots of XML configuration and tell it virtually everything about your project(s). It will then run through your build script and do exactly what you told it to do.

Maven is FUNDAMENTALLY DIFFERENT!

Maven takes an approach of convention over configuration. If you follow the maven conventions (for example, putting your flex source code directory at /src/main/flex), then the Flex-Mojos plugin will just know where to find your code and what to do with it.

Adobe FlashBuilder does quite a a lot of configuration behind the scenes when building a Flex project. Things like building against framework RSLs instead of merging into code are handled seamlessly in FlashBuilder. Those aren’t really defaults in Flex-Mojos, so let’s take a look at what it takes Flex-Mojos to make a large project do some of the nice things FlashBuilder does for us.

The core of a Maven project is the pom.xml file. The Project Object Model files tweak what Maven does already by convention. If you don’t specify a config option, no big deal… Maven will just use its default.

In the project for 3dvideomap.com, I’m using the following technology stack. MySQL database, Hibernate for ORM to the tables, Spring Framework, GraniteDS for Flex Remoting, and Swiz for my Flex microarchitecture.

Folder structure is an important convention to Maven.

The main parent pom.xml file contains many of my variable properties that I’ll use later. It also tells Maven what child project modules to build.

<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.swiftmako.freshchamber</groupId>
    <artifactId>freshchamber-aggregator</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>FreshChamber Aggregator</name>

    <properties>
        <flash.major>10</flash.major>
        <flash.minor>2</flash.minor>
        <flash.revision>0</flash.revision>
        <flex.sdk.version>4.5.0.20967</flex.sdk.version>
        <flex.tlf.version>2.0.0.232</flex.tlf.version>
        <flex.osmf.version>1.0.0.16316</flex.osmf.version>
        <flex.mojos.version>4.0-beta-7</flex.mojos.version>
        <flexunit.version>4.0-rc-1</flexunit.version>
        <springframework.version>3.0.5.RELEASE</springframework.version>
        <graniteds.version>2.2.0.GA</graniteds.version>
        <hibernate.version>3.5.6-Final</hibernate.version>
        <aws.sdk.version>1.0.14</aws.sdk.version>
        <swiz.version>1.1.0</swiz.version>
        <googleanalytics.version>1.0.1.319</googleanalytics.version>
        <googlemap.version>1.20</googlemap.version>
    </properties>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <modules>
        <module>freshchamber-flex</module>
        <module>freshchamber-coupons</module>
        <module>freshchamber-webapp</module>
        <module>freshchamber-hibernate</module>
        <module>freshchamber-graniteds</module>
    </modules>
</project>

Let’s next take a look at the coupons project. This is a flex app related to the main 3dvideomap app in that it uses the same backend, but it just shows coupons at http://coupons.3dvideomap.com. It isn’t using any Flex Modules or other advanced features. It’s a simple Flex 4.5 app. Let’s take a look at it’s pom.

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>freshchamber-aggregator</artifactId>
        <groupId>com.swiftmako.freshchamber</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>com.swiftmako.freshchamber</groupId>
    <artifactId>freshchamber-coupons</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>swf</packaging>
    <name>freshchamber-coupons</name>
    <build>
        <sourceDirectory>src/main/flex</sourceDirectory>
        <testSourceDirectory>src/test/flex</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>com.savage7.maven.plugins</groupId>
                <artifactId>maven-external-dependency-plugin</artifactId>
                <version>0.5-SONATYPE-r116</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>resolve-external</goal>
                            <goal>install-external</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <createChecksum>true</createChecksum>
                    <skipChecksumVerification>false</skipChecksumVerification>
                    <artifactItems>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>framework</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>textLayout</artifactId>
                            <version>${flex.tlf.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/tlf/${flex.tlf.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>osmf</artifactId>
                            <version>${flex.osmf.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>rpc</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>charts</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>mx</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>spark</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>advancedgrids</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>sparkskins</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                        <artifactItem>
                            <groupId>com.adobe.flex.framework</groupId>
                            <artifactId>spark_dmv</artifactId>
                            <version>${flex.sdk.version}</version>
                            <packaging>swz</packaging>
                            <downloadUrl>http://fpdownload.adobe.com/pub/swz/flex/${flex.sdk.version}/{artifactId}_{version}.{packaging}</downloadUrl>
                        </artifactItem>
                    </artifactItems>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.sonatype.flexmojos</groupId>
                <artifactId>flexmojos-maven-plugin</artifactId>
                <version>${flex.mojos.version}</version>
                <extensions>true</extensions>
                <dependencies>
                    <dependency>
                        <groupId>com.adobe.flex</groupId>
                        <artifactId>compiler</artifactId>
                        <version>${flex.sdk.version}</version>
                        <type>pom</type>
                    </dependency>
                    <dependency>
                        <groupId>com.adobe.flex.compiler</groupId>
                        <artifactId>adt</artifactId>
                        <version>${flex.sdk.version}</version>
                        <scope>compile</scope>
                    </dependency>
                    <dependency>
                        <groupId>com.adobe.flex</groupId>
                        <artifactId>flex-fontkit</artifactId>
                        <version>${flex.sdk.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>com.adobe.flex</groupId>
                        <artifactId>rideau</artifactId>
                        <version>${flex.sdk.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>com.adobe.flex</groupId>
                        <artifactId>afe</artifactId>
                        <version>${flex.sdk.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>com.adobe.flex</groupId>
                        <artifactId>aglj40</artifactId>
                        <version>${flex.sdk.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>html-wrapper</id>
                        <goals>
                            <goal>wrapper</goal>
                        </goals>
                        <configuration>
                            <templateURI>folder:/html-template</templateURI>
                            <parameters>
                                <swf>${project.build.finalName}</swf>
                                <width>100%</width>
                                <height>100%</height>
                                <version_major>${flash.major}</version_major>
                                <version_minor>${flash.minor}</version_minor>
                                <version_revision>${flash.revision}</version_revision>
                                <bgcolor>#FFFFFF</bgcolor>
                            </parameters>
                            <htmlName>index</htmlName>
                            <targetPlayer>${flash.major}.${flash.minor}.${flash.revision}</targetPlayer>
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <configurationReport>true</configurationReport>
                    <sourceFile>index.mxml</sourceFile>
                    <swfVersion>11</swfVersion>
                    <compilerWarnings>
                        <warn-no-constructor>false</warn-no-constructor>
                    </compilerWarnings>
                    <metadata>
                        <title>Map Coupons</title>
                        <creators>
                            <creator>Andrew Westberg</creator>
                        </creators>
                        <publishers>
                            <publisher>Swift Mako Software Inc.</publisher>
                        </publishers>
                        <description>http://swiftmako.com</description>
                        <languages>
                            <language>EN</language>
                        </languages>
                    </metadata>
                    <keepAs3Metadatas>
                        <keepAs3Metadata>Bindable</keepAs3Metadata>
                        <keepAs3Metadata>Events</keepAs3Metadata>
                        <keepAs3Metadata>EventHandler</keepAs3Metadata>
                        <keepAs3Metadata>Inject</keepAs3Metadata>
                        <keepAs3Metadata>Autowire</keepAs3Metadata>
                        <keepAs3Metadata>Mediate</keepAs3Metadata>
                        <keepAs3Metadata>Dispatcher</keepAs3Metadata>
                        <keepAs3Metadata>PostConstruct</keepAs3Metadata>
                        <keepAs3Metadata>PreDestroy</keepAs3Metadata>
                    </keepAs3Metadatas>
                    <fonts>
                        <advancedAntiAliasing>true</advancedAntiAliasing>
                        <maxCachedFonts>20</maxCachedFonts>
                        <maxGlyphsPerFace>1000</maxGlyphsPerFace>
                        <managers>
                            <manager>flash.fonts.JREFontManager</manager>
                            <manager>flash.fonts.BatikFontManager</manager>
                            <manager>flash.fonts.AFEFontManager</manager>
                            <manager>flash.fonts.CFFFontManager</manager>
                        </managers>
                    </fonts>
                    <quality>0.90</quality>
                    <optimize>true</optimize>
                    <debug>false</debug>
                    <defaultBackgroundColor>0xFFFFFF</defaultBackgroundColor>
                    <themes>
                        <theme>${settings.localRepository}/com/adobe/flex/framework/spark/${flex.sdk.version}/spark-${flex.sdk.version}-theme.css</theme>
                    </themes>
                    <removeUnusedRsls>true</removeUnusedRsls>
                    <rslUrls>
                        <url>{artifactId}_{version}.{extension}</url>
                    </rslUrls>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>framework</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>framework_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>textLayout</artifactId>
                                    <version>${flex.tlf.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>textLayout_${flex.tlf.version}.swz</destFileName>
                                </artifactItem>
                                <!--
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>osmf</artifactId>
                                    <version>${flex.osmf.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>osmf_${flex.osmf.version}.swz</destFileName>
                                </artifactItem>
                                -->
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>rpc</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>rpc_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                <!--
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>charts</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>charts_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>mx</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>mx_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                -->
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>spark</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>spark_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                <!--
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>advancedgrids</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>advancedgrids_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>sparkskins</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>sparkskins_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                <artifactItem>
                                    <groupId>com.adobe.flex.framework</groupId>
                                    <artifactId>spark_dmv</artifactId>
                                    <version>${flex.sdk.version}</version>
                                    <type>swz</type>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                    <destFileName>spark_dmv_${flex.sdk.version}.swz</destFileName>
                                </artifactItem>
                                -->
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <!-- RSL dependencies -->
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>framework</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>textLayout</artifactId>
            <version>${flex.tlf.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>osmf</artifactId>
            <version>${flex.osmf.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>rpc</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>charts</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>mx</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>spark</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>advancedgrids</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>sparkskins</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>spark_dmv</artifactId>
            <version>${flex.sdk.version}</version>
            <type>swc</type>
            <scope>caching</scope>
        </dependency>

        <!-- standard dependencies -->
        <dependency>
            <groupId>com.adobe.flex.framework</groupId>
            <artifactId>flex-framework</artifactId>
            <version>${flex.sdk.version}</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>com.adobe.flexunit</groupId>
            <artifactId>flexunit</artifactId>
            <version>${flexunit.version}</version>
            <type>swc</type>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.graniteds</groupId>
            <artifactId>granite-essentials-swc</artifactId>
            <version>${graniteds.version}</version>
            <type>swc</type>
            <scope>internal</scope>
        </dependency>
        <dependency>
            <groupId>org.swizframework</groupId>
            <artifactId>swiz-framework</artifactId>
            <version>${swiz.version}</version>
            <classifier>sparkonly</classifier>
            <type>swc</type>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

The first thing you’ll notice is that I’m using the maven-external-dependency-plugin to download all of the flex framework .swz files and store them in my local maven repository. They need to be in my local repository so I can copy them to the output folder later on.

I’m then executing the flex-mojos-maven-plugin. You’ll notice that I specify an execution to run the html-wrapper goal. In that goal, I specify a template folder so it will use the html template generated by FlashBuilder instead of the Flex-Mojos default. You’ll may also notice that I’m _NOT_ specifying an execution for the compile-swf goal. That’s because this happens automatically by convention. I only need to specify parameters for what I’m changing from the defaults.

When you run your build using “mvn clean install”, Flex-Mojos will spit out a list of what RSLs are required for your .swf to run. Use this list and comment out any unneeded ones in the maven-dependency-plugin: copy goal. This ensures that you only copy those RSL swz files to the output folder that your swf actually needs.

Let’s next look at a more complex example. The freshchamber-flex project is another Flex app, but this one uses a Flex Module. The Admin Module only shows up in the app when an end user accesses it from mapdashboard.com. When a normal user accesses the map, we don’t want to have the overhead of all of that configuration and editing code, so we put it into a module and only load it on the occasion that an admin or advertiser wants to edit the database.

The only changed section is to our configuration where we tell it to compile a module and optimize it for our main swf. We also have to force certain RSLs to be loaded because they’re _only_ used by the Admin module.

                    <modules>
                        <module>
                            <sourceFile>com/swiftmako/freshchamber/views/AdminViewModule.mxml</sourceFile>
                            <optimize>true</optimize>
                            <finalName>AdminViewModule</finalName>
                            <destinationPath>com/swiftmako/freshchamber/views</destinationPath>
                        </module>
                    </modules>
                    <removeUnusedRsls>true</removeUnusedRsls>
                    <rslUrls>
                        <url>{artifactId}_{version}.{extension}</url>
                    </rslUrls>
                    <forceRsls>
                        <rsl>${settings.localRepository}/com/adobe/flex/framework/mx/${flex.sdk.version}/mx-${flex.sdk.version}.swc</rsl>
                        <rsl>${settings.localRepository}/com/adobe/flex/framework/charts/${flex.sdk.version}/charts-${flex.sdk.version}.swc</rsl>
                        <rsl>${settings.localRepository}/com/adobe/flex/framework/sparkskins/${flex.sdk.version}/sparkskins-${flex.sdk.version}.swc</rsl>
                    </forceRsls>

So, that’s the basics of making flex-mojos behave a bit like FlashBuilder. Below you’ll find an archive of all the various poms in my project. It will cover things like generating AS3 classes from Java classes for use in GraniteDS remoting. Post comments below if you have any questions.

POMs.zip

Post to Twitter

Posted by Andrew, filed under as3, Flex, Flexbuilder, Java, Maven. Date: May 19, 2011, 11:31 am | 5 Comments »

I’ve noticed that at times FlashBuilder and in my case, the FlashBuilder plugin for Eclipse seems to randomly lose its ability to work. In the past, I’ve had to completely rebuild my workspace and re-import all of my projects. I’ve also seen a number of posts about how you should

1.) shutdown flashbuilder
2.) in your workspace/.metadata/.plugins folder, delete the com.adobe.flexbuilder.codemodel folder
3.) restart flashbuilder

Unfortunately, this one didn’t work for me. If it works for you, great. My problem seemed to be some type of conflict with either the subclipse plugin, or the M2Eclipse plugin. The following worked for me. Hopefully this is useful to you.

1.) shutdown flashbuilder
2.) in your workspace/.metadata/.plugins folder, delete any folder containing “subversion”, or “maven” in its name.
3.) restart flashbuilder

Hopefully this helps you out. If not, best of luck rebuilding your workspace.

Post to Twitter

Posted by Andrew, filed under as3, Flex, Flexbuilder. Date: March 23, 2011, 10:48 am | 1 Comment »

Nothing is more annoying than loading up a full browser flex app that has a login screen… It looks like it’s ready so you start typing only to realize that doesn’t let you type anything in. Usually, this is because your SWF file doesn’t have the browser focus. Even if you’ve set the focus to a text input field in ActionScript, you can still fall victim to this issue.

I’d like to share a few techniques I’ve used to ensure that the Flex app is able to get the browser’s focus immediately after loading.

First, you need to ensure that your wmode is set to opaque inside your html script. This isn’t required for all browsers, but in my layman’s testing, Chrome seemed to need it. If you’re using swfobject (the default for a FlashBuilder 4 app), customizing your html template will look something like this.

        <script type="text/javascript" src="swfobject.js"></script>
        <script type="text/javascript">
            <!-- For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection. -->
            var swfVersionStr = "${version_major}.${version_minor}.${version_revision}";
            <!-- To use express install, set to playerProductInstall.swf, otherwise the empty string. -->
            var xiSwfUrlStr = "${expressInstallSwf}";
            var flashvars = {};
            var params = {};
            params.quality = "high";
            params.bgcolor = "${bgcolor}";
            params.allowscriptaccess = "sameDomain";
            params.allowfullscreen = "true";
            params.wmode = "opaque";
            var attributes = {};
            attributes.id = "${application}";
            attributes.name = "${application}";
            attributes.align = "middle";
            swfobject.embedSWF(
                "${swf}.swf", "flashContent",
                "${width}", "${height}",
                swfVersionStr, xiSwfUrlStr,
                flashvars, params, attributes);
            <!-- JavaScript enabled so display the flashContent div in case it is not replaced with a swf object. -->
            swfobject.createCSS("#flashContent", "display:block;text-align:left;");
        </script>

Next, you need to run a bit of javascript code to set the focus to your Flex app after it has loaded. There are a number of techniques floating around to do this from your html template, but I prefer the tried and true method of using ExternalInterface from inside your Flex app’s applicationComplete handler. That way, you’re absolutely sure everything has loaded and you’re ready to give yourself the focus.

protected function applicationCompleteHandler(event:FlexEvent):void
{
    //set focus to this app using javascript
    if(ExternalInterface.available)
    {
        ExternalInterface.call("eval", "document.getElementById('" + ExternalInterface.objectID + "').tabIndex=0");
        ExternalInterface.call("eval", "document.getElementById('" + ExternalInterface.objectID + "').focus()");
    }
}

Setting the tabIndex to a value is another one of those quirky Chrome requirements. I found it on a bug report for Google Chrome and it seems to work for me. http://code.google.com/p/chromium/issues/detail?id=27868#c15 I’ve tested this code in Firefox, IE, and Chrome. It should work in Safari as well, but I haven’t tested that one.

Post to Twitter

Posted by Andrew, filed under as3, Flex. Date: December 30, 2010, 6:42 pm | 14 Comments »

When you layer Bitmap images on top of each other in Flash/Flex/AIR, an interesting thing happens. Mouse events are captured by the top-most image and aren’t passed through to anything underneath. Even when hovering over, or clicking on transparent areas, the events just don’t get through. Moses over at MosesSupposes created an InteractivePNG solution that does some interesting mouse handling and hit detection.

In the above example (click the image, right-click to view src), I have two transparent png bitmaps layered on top of each other. On the left side, I’m using the standard BitmapImage tag. When you hover over any part of the images (even transparent areas), you’ll notice that you get the SNOWFLAKE! tooltip. Since the snowflake is on top, it is capturing all mouse activity so nothing gets through to Santa underneath.

On the right, I’m using BitmapSprite. When you have a Sprite that is rendered using graphics drawing, mouse events are only captured in areas where the graphics object has physically been drawn on. BitmapSprite is designed to take an input Bitmap, convert it into 1-pixel tall rectangle bitmap fills that are drawn to the sprite’s graphics object. In this manner, any transparent pixel doesn’t capture mouse events and everything is passed down to objects beneath. When you hover over a snowflake pixel, you get the tooltip SNOWFLAKE!, and when you hover over santa, you get a SANTA! tooltip. With this solution, we’re also caching the result as a bitmap so we only pay the rendering penalty once. No mouse-handling or hit detection kung-fu necessary. Nifty!

Post to Twitter

Posted by Andrew, filed under as3, Flex. Date: December 1, 2010, 12:07 pm | 7 Comments »

On a project I’m working on, I need to have semi-transparent containers to hold controls over top of screen-filling bright and colorful background. While I’m not 100% satisfied with it, I think it will suffice for the time being.

I was originally going for an effect similar to the window borders in Windows 7 where the background image is blurred when viewed through the glass. I couldn’t find an easy way to do that effect in Flex 4. If you know of a way, please comment below.

Live Demo – Right-Click to View Source

Post to Twitter

Posted by Andrew, filed under as3, Flex. Date: July 31, 2010, 11:03 pm | 2 Comments »

I recently had an opportunity to give a talk at 360|Flex along with Randy Troppmann. Slides and code examples are posted below. Video of the session will come sometime in the unforeseen future as they have many hours of other sessions to post-process before getting to mine.

Presentation PDF (warning: large)

MileageBuddy (viewsrc enabled)

HeatmapExample (viewsrc enabled)

Test GPX file

Post to Twitter

Posted by Andrew, filed under 360 Flex, AIR, as3, Data, Degrafa, Flex, GPS. Date: March 11, 2010, 5:04 pm | No Comments »

I had the privilege of speaking to the Indy Flex User Group last night on the topic of Degrafa. While I’m not one of the architects or contributors to the project, I do make extensive use of Degrafa in most of my personal and consulting development work. Below is presentation material and example code I used during the presentation. All the examples have view-source enabled. Just right-click.

Presentation:
Degrafa_IndyFlex_User_Group.pdf

Simple Circle Example:
DegrafaExample1

Button Skinning Example:
DegrafaExample2

Component Development Speedometer Example:
DegrafaExample3

Axiis Example:
Smith Chart Example

ChromaPassword FXG Example:
ChromaPassword

MiniShapeDesigner Example (note, not mine):
MiniShapeDesigner

Post to Twitter

Posted by Andrew, filed under Degrafa, Flex. Date: February 24, 2010, 9:44 am | 3 Comments »

I’m slated to speak at 360|Flex San Jose on March 7-10 on the topic of GPS data and doing neat things with it in Flex/AIR. I’ll be co-presenting along with Randy Troppmann. Randy had been doing work for quite awhile in the GPS space with his work on runningmap.com. Runningmap.com is a website that allows runners/walkers/joggers/movers to track and catalog their accomplishments. They’ve also developed an iPhone application that can record your gps track as you run. It’s quite an honor to be able to present with such an accomplished developer.

I’ve been holding off on doing a blog post about 360|Flex until I could give you a sneak peak of the new prototypes of Swift GPS. Swift GPS is a wearable hardware device that will allow you to record GPX files to a USB thumbdrive that tracks your current position and movement. During my part of this session, we’ll go into how to take this GPX data and do neat things with it in Flex like overlay the data onto a 3D map. Other highlights will be doing a bit of heat-mapping using the GPS data.

This conference WILL SELL OUT, so don’t be one of the hundreds of chumps posting regrets to your twitter account after the conference starts and you didn’t get in. Click on my 360|Flex badge on the right-side of the blog to sign up today. If I win the speaker suite for the most signups through my blog badge, I’ll give away a prototype device to one of the people who helped put me over the top. Don’t wait. Sign up today.

Post to Twitter

Posted by Andrew, filed under 360 Flex, Data, Flex, GPS. Date: February 9, 2010, 8:42 pm | No Comments »

GraniteDS is a free and open source (LGPL’d) alternative to Adobe® LiveCycle® (Flex™ 2+) Data Services for Java EE application servers. Java Message Service (JMS) is a technology designed for handling real-time Publish/Subscribe architectures of which Apache ActiveMQ is an implementer.

By default, the GraniteDS messaging architecture (called Gravity) uses its own internal simple Publish/Subscribe infrastructure. This works great if your front-end only needs to worry about handling clients from Adobe Flex, but what about if you need to send messages from other back end processes and have your flex app update in real-time?

The GraniteDS download package ships with a good example called graniteds_chat. It demonstrates a simple chat application between two or more Flex clients using the built-in Publish/Subscribe architecture of GraniteDS’s Gravity component. My goal is to use this example, but replace the default GraniteDS architecture with the more robust ActiveMQ architecture running in a separate process.

The reasons for doing so are several. First, ActiveMQ can handle persistent messaging. If my server crashes for some reason, I won’t lose messages that were currently in the queues/topics. Secondly, I want to demonstrate running ActiveMQ in a separate process. GraniteDS already has an Embedded ActiveMQ example on their wiki where it loads and runs inside the Tomcat container. I want to break it out as a completely separate process. This gives me additional future scalability if I need to run ActiveMQ on a different box entirely, or setup a multi-machine JMS infrastructure.

First, download the latest package of GraniteDS. Extract out the folder structure, and modify any of the env.properties file to point to your installed Flex SDK. My env.properties inside graniteds/examples/graniteds_chat looks like this:

#==============================================================================
# Granite Data Services Examples build properties (http://www.graniteds.org).
#==============================================================================

# Set 'FLEX_HOME' property to your flex3 sdk installation directory and
# 'FLEX_TASKS_JAR' to your flexTasks.jar location.
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3 Plug-in/sdks/3.4.0.9271
FLEX_TASKS_JAR=${FLEX_HOME}/ant/lib/flexTasks.jar

# Set 'SERVER_HOME' property to your JBoss installation directory and
# 'SERVER_HOME_DEPLOY' to your JBoss deploy directory.
#SERVER_HOME=/jboss-4.2.3.GA
#SERVER_HOME_DEPLOY=${SERVER_HOME}/server/default/deploy

# Alternatively, set 'SERVER_HOME' property to your Tomcat installation
# directory and 'SERVER_HOME_DEPLOY' to your Tomcat deploy directory.
SERVER_HOME=C:/apache-tomcat-6.0.16
SERVER_HOME_DEPLOY=${SERVER_HOME}/webapps

Next, run ANT inside the graniteds_chat folder. It will pick up everything that needs to be compiled from the parent folders and deploy a war package to your local tomcat installation. Before starting tomcat and watching the default chat example in action, you’ll first have to modify Tomcat a bit so that it can support non-blocking IO. GraniteDS recommends using either the HTTP APR or the NIO connector in Tomcat to enable non-blocking IO. For our purposes, we’ll use the NIO connector for Tomcat. Inside the Tomcat conf folder, edit server.xml and look for a line like the following:

<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

Modify this section so that Tomcat can use the NIO Http connector.

<Connector port="8080"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000"
           redirectPort="8443" />

After that change is complete, start Tomcat and navigate with two separate browsers to http://localhost:8080/graniteds-chat. Login with separate usernames and make sure that you can chat successfully between the two browsers. You should see something like the following:
Andrew1_chat
Andrew2_chat

Next, let’s go about integrating ActiveMQ. Download and install ActiveMQ in its own folder. Browse to the bin folder and run activemq.bat. This should be all we need to do on the ActiveMQ side of things. Next, we need to configure GraniteDS to talk to ActiveMQ via JMS instead of its internal messaging architecture.

Stop Tomcat and drill into the installed chat application and modify services-config.xml. Mine is located at C:/apache-tomcat-6.0.16/webapps/graniteds-chat/WEB-INF/flex/services-config.xml. Before editing, you’ll notice that it’s using its own internal SimpleServiceAdapter.

<?xml version="1.0" encoding="UTF-8"?>

<!--
 GRANITE DATA SERVICES
 Copyright (C) 2007-2008 ADEQUATE SYSTEMS SARL

 This file is part of Granite Data Services.

 Granite Data Services is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation; either version 3 of the License, or (at your
 option) any later version.

 Granite Data Services is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, see <http://www.gnu.org/licenses/>.
-->

<services-config>

    <services>
        <service id="messaging-service"
           class="flex.messaging.services.MessagingService"
           messageTypes="flex.messaging.messages.AsyncMessage">
            <adapters>
                <adapter-definition id="default" class="org.granite.gravity.adapters.SimpleServiceAdapter" default="true"/>
            </adapters>

            <destination id="gravity">
                <channels>
                    <channel ref="my-gravityamf"/>
                </channels>
            </destination>
        </service>
    </services>

    <channels>
        <channel-definition id="my-gravityamf" class="org.granite.gravity.channels.GravityChannel">
            <endpoint
               uri="http://{server.name}:{server.port}/{context.root}/gravity/amf"
               class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

You’re going to want to modify this services-config.xml file so that it uses ActiveMQ and the JMS Adapter.

<?xml version="1.0" encoding="UTF-8"?>

<!--
 GRANITE DATA SERVICES
 Copyright (C) 2007-2008 ADEQUATE SYSTEMS SARL

 This file is part of Granite Data Services.

 Granite Data Services is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation; either version 3 of the License, or (at your
 option) any later version.

 Granite Data Services is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, see <http://www.gnu.org/licenses/>.
-->

<services-config>

    <services>
        <service id="messaging-service"
           class="flex.messaging.services.MessagingService"
           messageTypes="flex.messaging.messages.AsyncMessage">
            <adapters>
                <adapter-definition id="default" class="org.granite.gravity.adapters.JMSServiceAdapter" default="true"/>
            </adapters>

            <destination id="gravity">
                <channels>
                    <channel ref="my-gravityamf"/>
                </channels>
                <properties>
                    <jms>
                        <destination-type>Topic</destination-type>
                        <connection-factory>java:comp/env/jms/flex/TopicConnectionFactory</connection-factory>
                        <destination-jndi-name>java:comp/env/jms/discussion</destination-jndi-name>
                        <destination-name>discussion</destination-name>
                        <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
                        <transacted-sessions>false</transacted-sessions>
                        <!--
                             JNDI environment. Specify the external JNDI configuration to
                             access a remote JMS provider.
                        -->
                        <initial-context-environment>
                            <property>
                                <name>Context.PROVIDER_URL</name>
                                <value>tcp://localhost:61616</value>
                            </property>
                            <property>
                                <name>Context.INITIAL_CONTEXT_FACTORY</name>
                                <value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
                            </property>
                        </initial-context-environment>
                    </jms>
                </properties>              
            </destination>
        </service>
    </services>

    <channels>
        <channel-definition id="my-gravityamf" class="org.granite.gravity.channels.GravityChannel">
            <endpoint
               uri="http://{server.name}:{server.port}/{context.root}/gravity/amf"
               class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

You may have noticed that the GraniteDS JMSServiceAdapter uses JNDI to find and connect to JMS. Since Tomcat has a valid JNDI container, let’s define the necessary info in Tomcat. You can add the JNDI info inside tomcat’s conf/context.xml file.

<?xml version='1.0' encoding='utf-8'?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>

    <!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
   
    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
   <Manager pathname="" />
   -->

    <!-- Uncomment this to enable Comet connection tacking (provides events
        on session expiration as well as webapp lifecycle) -->
    <!--
   <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
   -->
   
    <Resource name="jms/flex/TopicConnectionFactory"
              type="org.apache.activemq.ActiveMQConnectionFactory"
              description="JMS Connection Factory"
              factory="org.apache.activemq.jndi.JNDIReferenceFactory"
              brokerURL="tcp://localhost:61616"
              brokerName="activeMQBroker" />
    <Resource name="jms/discussion"
              type="org.apache.activemq.command.ActiveMQTopic"
              description="Discussion topic for GraniteDS"
              factory="org.apache.activemq.jndi.JNDIReferenceFactory"
              physicalName="discussion" />
   

</Context>

Finally, copy activemq-all-5.3.0.jar from your ActiveMQ install folder to apache-tomcat-6.0.16/webapps/graniteds-chat/WEB-INF/lib. This will ensure that JNDI has access to the ActiveMQ classes it needs to communicate to the ActiveMQ JMS broker. Start tomcat and refresh your two browser windows running the chat application. Send a few messages back and forth to ensure everything is working.

Now, we need to prove to ourselves that messages are indeed going through ActiveMQ and not just through GraniteDS. Open a new browser tab to the ActiveMQ monitor page http://localhost:8161/admin/topics.jsp . Make sure that the topic "discussion" exists and has some enqueued and dequeued messages.
active_mq_topics

If you really want to test out the system, create a simple Java program that can send a JMS message to ActiveMQ. Make sure the resulting message ends up in your Flex app.

import javax.jms.Connection;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;

public class ChatTest
{

    /**
     * @param args
     */

    public static void main(String[] args)
    {
        try
        {
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
            Connection connection = connectionFactory.createTopicConnection();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // create our request and response queues
            Topic topic = session.createTopic("discussion");
            // and attach a producer to the topic
            MessageProducer producer = session.createProducer(topic);
            // and start your engines...
            connection.start();
           
            // now send our test JMS message
            Message message = session.createObjectMessage(new String("Hello from JMS"));
            producer.send(message);
           
            //sleep a bit to make sure our message sent ok
            Thread.sleep(10000);
           
            session.close();
            connection.close();
            System.out.println("Sent message ok");
        }
        catch (Throwable e)
        {
            e.printStackTrace();
        }

    }

}

Post to Twitter

Posted by Andrew, filed under Data, Flex. Date: November 16, 2009, 11:44 am | 4 Comments »

« Previous Entries