Jon Williams (jon)

Website: http://shovemedia.com


 

Posts by Jon Williams (jon):


Removing Trace and Logging Statements for Production

I'm saving these recipes here so I can bookmark, and I'm sure someone else will find them useful.

I decided to convert calls to trace or my logging framework to use conditional compilation so that they won't be included when I do a production build, but are included when I do a debug build.

The Flex compiler supports this by allowing you to set a variable at compile-time, eg:

-define=CONFIG::debug,true

In your code, simply wrap the code to be conditionally compiled in curly braces, prepended with this variable, eg:

CONFIG::Debug
{
	trace( 'Debug' );
}

I spent some time writing a regular expression that finds trace statements, and wraps them in this block. In plain English, start at the beginning of the line, match zero or more whitespace, the word "trace", zero or more whitespace, an open parenthesis, the rest of the line.

Search for: ^([ \t]*)(trace[ \t]*\(.*)$
Replace with: $1CONFIG::debug { $2 }

FDT's internal parser doesn't like the conditional compilation syntax, so we need to change our replacement text slightly to wrap that part in markers instructing it to ignore them.

Search for: ^([ \t]*)(trace[ \t]*\(.*)$
Replace with: $1/*FDT_IGNORE*/ CONFIG::debug /*FDT_IGNORE*/ { $2 }

The same recipe for any call to logger, eg: logger.debug, logger.info, logger.warn, etc.

Search for: ^([ \t]*)(logger\..*)$
Replace with: $1/*FDT_IGNORE*/ CONFIG::debug /*FDT_IGNORE*/ { $2 }

Masking TLF (Text Layout Framework)

Just a quick note to point out a bug (?) I found today while working with the ActionScript 3 Text Layout Framework.

I wanted to add a mask to the Sprite container I'd passed to my TLF ContainerController. Whatever it is that happened wasn't correct: there was masking occurring, but the shape I was using as a mask was (incorrectly) visible on the stage. Rolling over my buttons caused redraw of stage regions, and my TLF text in those areas would go from invisible to incorrectly masked.

After trying several things, I pulled an old-school trick out of my bag: nest the Sprite I was passing as a container to the ContainerController in another holder Sprite, and apply the mask to the holder instead. From there, everything worked as expected.

Note: I further tracked this down to be caused by the ContainerController being created smaller than the text stuffed inside. Only then do I have problems with masking. YMMV.

(See the original post)

I messed around with this quite a bit but was never able to get things to work exactly as I'd hoped. I even got as far as using the XmlTask Ant extensions to modify the launch file on the fly. Unfortunately, I couldn't figure out how to get Eclipse to reload the modified configuration without completely restarting, so that path resulted more or less in a dead end.

What I did figure out is how to make your launch configurations shareable via SVN, so that only one team member (yours truly) has to deal with builds and launch configurations. Everyone else can update from SVN and run ANT tasks without being familiar with all these grisly details.

The last post mentioned (with no small amount of grumbling) that you have to change the JRE setting to "Same JRE as Workspace" on every single build target you create (or rename). This is incredibly annoying. If you go to the same dialog: Run >> External Tools >> External Tools Configurations...

Check out the Common Tab. Here you can pull the launch file configuration into the project and out of the workspace by selecting "Shared file" instead of "Local file". This lets you share launch configurations with the rest of the team.

If you wish, you can create a template for new build targets. If you get the file naming convention right, you should be able to get away with defining new build targets without ever needing to visit the External Tools Configurations dialog. I've got a mixed OS X / Windows Team, so I made some modifications to Eclipse's default launch file output. In short, I removed explicit references to my OS X default JVM.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ant.AntLaunchConfigurationType">
<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/PROJECT_NAME/build.xml"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="PROJECT_NAME"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_ANT_TARGETS" value="BUILD_TARGET_NAME"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/PROJECT_NAME/build.xml}"/>
<stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/>
</launchConfiguration>

Note the spots where you should substitute your own PROJECT_NAME and BUILD_TARGET_NAME.

I tried setting the DEFAULT_VM_INSTALL to true, and it worked, but Eclipse insisted on mucking with it as soon as I visited the External Tools Configurations dialog again.

I'm storing my launch files in .settings and setting the filename to:
PROJECT_NAME BUILD_FILE_NAME.xml [BUILD_TARGET_NAME].launch

Your mileage will almost certainly vary.

Use FDT Folder Path Variables in ANT

My latest FDT project has a lot of sub-projects. We've got a small team of mixed Mac and Windows users, so we don't want to assume any particular project directory structure if we can help it. The original thinking was to export SWC files for each sub-project, copy them to the appropriate projects for which they are dependencies, wash-rinse-repeat. I'd assumed I'd be able to use FDTs pre/post-build ANT tasks for this, but unfortunately you only get that tab when building to SWF, not for SWCs.

This brought about the idea of putting the entire build routine in ANT, but I hit another roadblock: I want the team members to be able to define folder path variables using the FDT Eclipse Linked Libraries mechanism. To access these:

  • Right-click the FDT project folder >> Properties
  • Choose FDT Build Path
  • Click "Add Linked Libraries..."
  • Click "Add..."

This window lets you manage folder and SWC paths in an abstract way so that build files and classpaths can use the variable instead of a hardcoded path. As long as each team member uses the same variable name, you can share build scripts and complicated classpath settings. All you have to do is tell SVN to ignore the .project file, and everything should be golden.

Ok, so how do you get access to those variables within your ANT scripts? Like this:

<project name="SampleProject" default="compileAll" basedir=".">
    <!-- Import FDT path variables for use as ANT properties -->
    <property file="${basedir}/../.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs"/>

    <!-- etc -->
</project>

(I know the wordpress template makes that impossible to read. Cut paste it.) There are some fairly radical assumptions at work here, and I'm not enough of an Eclipse expert to know if it will work in all environments (plus, there are a LOT of variations in environments out there). The idea, however, is that all projects are direct children of the workspace. Therefore, backing up one level from the basedir should put you in the root of the workspace. From there, the path to the aforementioned FDT path variable property file should be as listed. That, of course, assumes that your Eclipse projects are using the default path. If they aren't, you're on your own. I wasn't able to find a workspace variable that gets passed to ANT automatically, but you should be able to set one yourself by going to the menu Run >> External Tools >> External Tools Configurations... In the Arguments textbox, put:

-Dworkspace_loc=${workspace_loc}

That would let you use the workspace location directly:

    <!-- Import FDT path variables for use as ANT properties -->
    <property file="${workspace_loc}/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs"/>

You'll need to become at least passingly familiar with the External Tools dialog because you'll be visiting it more often than you'd like in order to bypass a minor oversight by the FDT team (IMHO). Go to the JRE tab and switch your Runtime JRE to "Run in the same JRE as the workspace". If I recall correctly, you'll have to do this every time you create a new build target (sigh). If you know a better way, by all means leave a comment.

This configuration also assumes that the build.xml file is a direct child of the project folder, ie basedir="." If this isn't the case you should now have enough info to pass the project folder as well. (Hint: try project_loc or project_path)

Once you've done this, your FDT path variables can be accessed by prefixing with "pathvariable". For example:

<echo message="${pathvariable.AS3SWF_SRC}" />

It took a little Googling on my part to find how to properly export a SWC under ANT without resorting to a complete re-configuration of the classpath. FDT includes an ANT task specifically meant for this purpose. An example looks like:

<target name="generateSwc2">
  <fdt.launch.library
    projectname="${ant.project.name}"
    debug="false"
    target="${swcdir}/${swcfilename}"
  />
</target>

Getting all the variables set up is left as an exercise for the reader. For some help, try Boostworthy's seminal article.

You should now have all the pieces required to compile and copy SWCs without having paths to specific projects or tools littering your ANT build files. Good luck!

In Defense of Dependency Injection

I've been rolling the RobotLegs framework into a new project, but not all the team members are sold on the idea, mostly because it represents third-party code that's complex and difficult (impossible?) to understand just by reading over the source.

I realized I was having a difficult time articulating the advantages to the approach, and specifically, the fact that the closest alternatives are every bit as complex and headache-inducing once you get down into the details.

So, what follows is a bit of a thought experiment using a very simple case. Hopefully, by the end, you'll see that what's true for a single, simple case is also true for multiple, complex cases, and in the end, it actually makes good sense to have a framework handle these things.

Imagine first, a Main class. There's only one Main across the entire codebase. Now imagine classes A, B, C, etc (there are a lot of them) that all use a ColorManager class. A, B, C are separate SWFs, so it makes sense for ColorManager to be compiled into Main, and somehow linked into the A, B, and C modules, but not compiled into them. This in itself can be a bit tricky, and there are a few different approaches, with assorted pros and cons.

Our first thought is to compile against a linked SWC containing the concrete ColorManager class. That will keep the actual implementation out of our modules, but any change to ColorManager will require re-compiling every SWF that uses it. Less than ideal.

We could say:

public class A
{
  private var colorManagerClass:Class = getDefinitionByName("ColorManager");
  private var colorManager:IColorManager = new colorManagerClass();
}

It gets the job done, and it's easy to understand. As long as the interface stays in synch, it's not too hard to make changes to the concrete ColorManager class without recompiling the module (and all the others). That synched interface is a pretty big caveat, but we'll find that most (all?) approaches share it. We could forego the interface completely, but at the expense of loosing very valuable strong-typing and code completion.

The downside would be that the concrete class absolutely must be "ColorManager" -- even though we've gone to the trouble of creating an interface (which would/should/could allow other implementations) were stuck with the one-and-only ColorManager returned by getDefinitionByName. The simplest way to make this dynamic? Pass in the String representing the class name. You know what that's called? Dependency Injection. If you're willing to pass in a String, you might as well pass in the class itself.

Another quibble with this technique is that it relies on something known as a "magic string." The compiler doesn't have a way of warning you that this class does or doesn't exist, or that it correctly implements the IColorManager interface. You'll need to rely on runtime warnings for that. It's very easy to misspell or miscapitalize the package/classname breaking everything.

Ok, my (completely competent) team member suggests, we can provide a setter for the class. That might look like:

public class A
{
  private var colorManager:IColorManager

  public function setColorManagerClass (colorManagerClass:IColorManager)
  {
    this.colorManager = new colorManagerClass();
  }
}

Again, this is merely another way of doing Dependency Injection. The real question is whether you'll be using a framework to provide dependencies automatically. We'll come back to address this separately.

What about having our class request the class at runtime, rather than relying on the fact that it will be injected. Something like this:

public class A
{
  private var classLocatorServiceInstance:ClassLocatorService;
  private var colorManager:IColorManager;

  public function initColorManager()
  {
    var colorManagerClass:Class = classLocatorServiceInstance.getConcreteClass(IColorManager);
    this.colorManager = new colorManagerClass();
  }
}

This implementation, while quite valid, raises more questions than it answers. Who calls initColorManager? How can it be sure the ClassLocatorService is ready to provide ColorManager? And just where does this ClassLocatorService come from any way? It's a singleton? How is that enforced? Did you notice how we're requesting the concrete class? Via its interface. We've got the same problem with allowing only one implementation of IColorManager. In order to allow multiple implementations, we're back to using a magic String.

Flash and Kerning

You're probably a special breed if you're reading this. The nuance of this feature along with Flash's implementation peculiarities mean that most people just don't fool with it for more than ten minutes.

This one bit me before, so I'm making a note to myself:

If you're using StyleSheets in Flash via ActionScript (I didn't try loading a textfile), the kerning property does not take a Boolean true/false as the documentation insists. I only works if you use a Number 1/0. The font tag will let you use a Boolean.

Also, since you're so special, I have a tool I've been working on to compile fonts into loadable libraries on the server. It will attempt to include kerning information if possible. The only other way to do that (to my knowledge) is via the Windows IDE.

Try the beta font to swf compiler here.

SVN for Eclipse Quickstart for Beginners

This is well trod ground, but I didn't find a lot of really basic info about what to do, what not to do, and generally how to get your feet wet with SVN, and what to do when suddenly you're up to your neck. I'm certainly no expert on source control, but sometimes a fellow beginner can explain things more gently.

The first hurdle is getting the plugin(s) installed. I'm personally not sure which are best. Try them for yourself and see. At the time of writing, here are the Eclipse update URLs to add for installing several SVN-for-Eclipse clients. I like the subversive icon set on the Team menu specifically, so I'm including it even though the download site was an absolute pain. I had to download to my local drive first. Your mileage may vary.

  • http://eclipse.svnkit.com/1.3.x/
  • http://subclipse.tigris.org/update_1.6.x
  • http://download.eclipse.org/technology/subversive/0.7/update-site/ (if it works for you, didn't for me) I downloaded from:
  • http://www.eclipse.org/downloads/download.php?file=/technology/subversive/0.7/builds/Subversive-incubation-0.7.8.I20090904-1300.zip">Subversive-incubation-0.7.8.I20090904-1300.zip

Here're those pretty icons. I like them because they help remind me what direction the operation goes in. "Update" Does that mean update my stuff or their stuff? "Commit" Which direction is that?

  1. Go to the Window Menu >> Software Updates
  2. Go to the "Available Software" tab
  3. Add Site
  4. Enter the URL or local hard drive path
  5. Click OK
  6. Click the Refresh button on the list of  update locations.
  7. Wait for Eclipse to do its thing.
  8. Check off the subclipse, subversive, and / or svnkit bits you'd like to install.
  9. Click the Install button.
  10. Restart Eclipse.

Next hurdle is getting logged in. If you're also setting up your first SVN server, best of luck to you. We're not covering that here today. You probably want to be familiar configuring an SVN client before you try setting up a server as the access control can be really tricky, especially if it's remote, which, usually it is.

Window >> Show View... >> Other... >> SVN Repositories

Right-click (Control-click Mac) >> New

Set yourself up a connection with the URL, username, and password provided by your system administrator.

Say a prayer.

With any luck, you'll be browsing the project tree!

Checking out a New (to you) Project

Browse to a project you'd like to check out in the SVN Repositories window.

Right-click (Control-click Mac) >> Check out

What you select from here depends on whether your team members have agreed to share their entire Eclipse project workspace (the second bullet) or if individual team members are managing their own Eclipse projects on some level (the default bullet). Confer with someone already on the project.

Click Finish!

Take a moment and look at the SVN Settings for icons in the Explorer view. You'll actually learn a little about the terminology you'll soon master, and begin to be able to recognize the state of a file by glancing at it's icon and know what step needs to be done next.

Eclipse Menu >> Preferences... >> Team >> SVN >> Label Decorations

Go to the Icons tab.

If you toggle the checkboxes back and forth, you'll be able to see which labels go with which icons. This is really useful to know.

Adding a New (local) Project to the SVN Repository

Chances are, if you're just starting, your first project already exists, and you can skip ahead for now. If not:

Right-click your main project folder in the Explorer view >> Team >> Share Project

SVN >> Next

Choose your repository >> Next

I think "Use project name" is best. Next

Get used to entering comments that describe what you're checking in. This dialog gives you the opportunity to do that. It's probably pre-populated with something brief about sharing this project.

Click Finish!

The project will be analyzed and you'll be given the opportunity to de-select any files you don't wish to add to the repository.

Once the files are successfully shared via SVN, your files' icons should change to reflect their state with respect to SVN. This comes in handy. You'll also see a version number appended to your filenames. You can modify details in Eclipse preferences.

Adding a New (SVN) Project to Your (local) Workspace

Use the SVN Repositories view to browse to the project's root folder.

Right-click and choose "Check Out"

If you already have a project at the default location, you'll be asked to reconcile conflicts / overwrite files. Otherwise, you should have a brand new project in your workspace!

Getting the Latest Updates

Ok, someone's made a change to files you've previously checked out, and you need to update your local copy.

Always do this before making changes (see the next section). It will keep you and the rest of your team from spending a lot of time in the last section.

Right-click the project folder >> Team >> Update

Commits - Making Changes

You've changed some things, and you're ready to check them into the repository.

Right-click the project folder >> Team >> Commit

(It also doesn't hurt to update again after a commit.)

Adding Files

Some SVN clients require you to "Add" files to the repository before you "Commit" your changes. It's a bit like when you "add," you only add an empty entry. It's the commit that sends the data. This separation keeps you from thinking you're adding a brand new file, when in fact there's already an existing file with that name in the repository. Eclipse's plugins seem to handle this automatically.

Deleting Files

Delete the file(s) as you normally would.

Perform a commit on the containing folder. SVN should detect the change and update the remote repository.

When Things Go Wrong (and They Will)

I can't Update because I've got a conflicting Local File.

(or)

I can't Commit because there's a conflicting Remote File.

Sometimes, the conflicting changes can be (and are) resolved automatically. Sometimes, it'll require more manual effort. The parent folder will have some new files with names based on the one having a conflict.

MyClass.as (your local working copy -- it probably contains some diff comments now)
MyClass.as.mine (an untouched copy of your local version)
MyClass.as.rVERSION_NUMBER (the last good copy known by both the local machine and SVN)
MyClass.as.rVERSION_NUMBER+ (SVN's latest copy)

If you don't see these in your Explorer view, right-click the parent folder and choose Refresh.

Still nothing? Do an Update and repeat.

There should be enough here to help you resolve the issue and no risk of loosing anything.

Edit MyClass.as so that it contains whatever you now believe to be correct. You can use Eclipse Synchronization perspective, an external diff / merge tool (I like DeltaWalker), or the standard Eclipse editor.

To use Eclipse's tools, Right-click the file with conflicts >> Team >> Edit Conflicts

The local working copy should appear in a left pane with the most recent repository version on the right. The goal here is to get the local working copy to contain the "correct" code. Merge anything from the SVN version on the right that should be present in the new version. (Eclipse will not allow you to make any edits to the SVN version.) When you're done, Save. Close the Edit Conflicts tab. Your changes should migrate to the local working file. Note: If you Edit Conflicts again (before Committing), you may loose the merges you made the first time. This is a bug or a feature depending on how you think about it.

I set up a custom DeltaWalker launcher shell script as an external diff / merge tool. Let me know if you'd like to try it. I doubt there's much (any?) functionality that isn't available in Eclipse's Edit Conflicts, but I really like DeltaWalker's GUI, color scheme, etc.

Right-click the file >> Team >> Mark as Merged (or Mark Resolved...) Click Ok.

Immediately Commit changes! You aren't finished resolving the conflict until you do so!

The Three-Way Merge

What you've probably worked with so far is a two-way merge. SVN also keeps track of the latest known good ancestor to those two files. In the Edit Conflicts view, click the icon in the toolbar to enable this pane. I rarely find this necessary, but it's good to know it's there if you need it.

Import / Export Existing Projects in FDT (Eclipse)

I've been using FDT for awhile, and convinced the project manager on a new job to purchase licenses for the whole team. I'd not used FDT in a team environment before, let alone with FDT / Eclipse noobs. That makes me the de-facto expert, and I was asked how to import a project I sent over. I'd assumed there was just a menu item for that, or it was a google-able thing. Well, you know what they say about assumptions.

I'm going to include export directions for completeness, but I imagine you could just zip up the whole project folder and get away with it in many cases. Using the export feature will capture source libraries located outside the project folder in your filesystem hierarchy and give you the opportunity to select/deselect the ones you specifically want included/excluded. With great power comes great responsibility!

Exporting

  1. Right-click your project in the Flash Explorer. Choose "Export..."
  2. Choose General >> File System
  3. Click Next.
  4. Choose a directory to export to. A top-level folder will be created within it having a name matching the project name.
  5. Deselect any libraries (etc) you specifically want to exclude from the project tree. Coordinate with your team members!
  6. Click Finish.

That covers exporting the project and any source libraries you want to package with it. One thing remaining -- the launch configuration(s) -- those settings cover what classes get compiled into what swf files, custom compiler arguments, etc.

  1. Right-click your project in the Flash Explorer. Choose "Export..."
  2. Choose Run / Debug >> Launch Configurations
  3. Click Next.
  4. (Your choices here depend on how you've been using FDT.) Expand "FDT AS3 Application" and select the appropriate file(s).
  5. Browse to the same folder you selected in step 4 of the project export process above.

Importing

Before you begin, especially if you're following along and you've just done this on your own machine, be sure you don't have an existing project with the same name as the one you'll be importing. Eclipse will complain: Some projects were hidden because they exist in the workspace directory. To change it:

  1. Make sure the project is open. (You can't rename a closed project)
  2. Right click the conflicting project.
  3. Enter a new name.
  4. Click OK.

Eclipse will do all the hard stuff for you.
Now to do the actual importing.

  1. File Menu >> Import
  2. General >> Existing Projects into Workspace
  3. Click Next
  4. Browse to the exported project folder and select it.
  5. Eclipse will list all the projects it finds within. Verify the one(s) you want is/are checked.
  6. Personally, I check off "Copy projects into workspace" at the bottom. Make sure you don't have any conflicting projects with the same name. See above. It's helpful to understand Eclipse workspaces when deciding which is best for you.
  7. You should now have a new project in your workspace.
  8. Ideally everyone on the team will want to use the same project name. Change it using the instructions above.
  9. Project Menu >> Clean...

Import your Run Configuration file using the same steps, but you'll probably want to read the next section first.

Mangled Run Configurations

Your Run Configurations will be un-usable if you import with a different project name (defined by the folder name the files were in) than the project was exported with or if you simply renamed the project.

If you don't do this, you will likely have problems with your Run Configurations and have no easy way to fix them or any reasonable explanation what's wrong (Null Pointer Exception). For your reference, the original launch configuration files are located in:

{workspace}/.metadata/.plugins/org.eclipse.debug.core/.launches/{PROJECT_NAME}.launch
Edit the line that reads: <stringAttribute key="PROJECT_NAME" value="MODIFY_ME"/>

I really wish this were part of the project export...

Comments on this article are particularly encouraged. If you find a better technique or an inaccuracy, please let me know!

I've been looking for a way to force "Same JRE as Workspace" as the default in my ANT build targets under FDT / Eclipse. I haven't figured it out yet, but I finally found where this setting is stored:

{workspace}/.metadata/.plugins/org.eclipse.debug.core/.launches/myProject myBuildfile.xml.launch

Remove the entries for org.eclipse.jdt.launching.JRE_CONTAINER & org.eclipse.jdt.launching.MAIN_TYPE, and Eclipse should use the workspace's JRE.

I'm sure someone could take this information and throw together a plugin / fix? The powerflasher team would honestly make the best candidates. (Hint! hint!)

Debugging The Dreaded “Null Object Reference”

Add -verbose-stacktraces=true to your mxmlc compiler arguments to get the line number of the dreaded "Cannot access a property or method of a null object reference."

How have I been programming ActionScript 3 for this long and not known this?