Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Creative tools for business
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection / Flex Developer Center /

Flex and Maven with Flexmojos – Part 2: The apprentice

by Justin J. Moses

Justin J. Moses
  • about.me/justinj

Content

  • Project setup
  • Finding dependencies
  • Customizing Flexmojos
  • Unit testing
  • Adding projects
  • Where to go from here

Created

17 October 2011

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
configuration deployment Flash Builder Flex

Requirements

Prerequisite knowledge

Intermediate to advanced Flex knowledge

 

Additional required other products

 

Apache Maven 3.0.3

  • Download

Flexmojos 4.0-RC2

  • Plug-in info (listing of all goals and configuration settings)

User level

Intermediate

Required products

  • Flash Builder (Download trial)

Sample files

  • View examples on Github

Welcome back to the series on Flex, Maven, and Flexmojos. Part 1 covered Maven itself: how it works, the terminology and conventions, and the Flexmojos plug-in. If you haven't read Part 1 and are new to Maven, I thoroughly encourage you to peruse it prior to reading this article, as there are many Maven conventions in place here that will be assumed knowledge.

In this article, I'm going to take you deeper into the world of Maven and Flexmojos and look at project setup, finding dependencies, configuration, unit testing, and adding projects to the build.

Note: In the previous article I mentioned that this article would also cover Flash Builder integration. I've decided to cover the topic in full, including the m2eclipse plug-in, in a separate article on Maven and Flash Builder.

Project setup

Folder structure

As you saw in Part 1, when you build Maven projects, you simply tell it—and hence the Flexmojos plug-in—where to find the root source folder via the following parameter in the POM:

<sourceDirectory> . . . </sourceDirectory>

This directory location itself is arbitrary; however, you will regularly see this convention in Flex projects:

<sourceDirectory>src/main/flex</sourceDirectory> <testSourceDirectory>src/test/flex</testSourceDirectory>

The typical folder structure, with some added files, looks like Figure 1.

Figure 1. Typical project layout
Figure 1. Typical project layout

Following this convention enables you to segregate your source properly from your unit tests (preventing their inclusion in your actual build). It also gives you somewhere (/src/main/resources) to keep non-source assets, such as resource bundles for different locales, CSS files, fonts, images, and other miscellaneous resources.

Using an archetype

Maven supports project templates, called archetypes. An archetype provides a basic project skeleton to help developers get started. Flexmojos provides these as well, helping you begin a new project.

Try generating a project from this simple archetype yourself:

mvn archetype:generate -DarchetypeRepository=http://repository.sonatype.com/content/groups/flexgroup -DarchetypeGroupId=org.sonatype.flexmojos -DarchetypeArtifactId=flexmojos-archetypes-application -DarchetypeVersion=4.0-RC2

After you run the command, Maven will download the necessary artifacts for the archetype and then ask you for the groupId , artifact , version and package (typically, the same as your groupId ). Once the download is completed, Maven (via Flexmojos) creates the basic structure for you.

Tip: An archetype is just another build artifact in Maven. The one above uses this POM to build.

What to check in to source control

One of the advantages of having a project management script—a POM—is that you have a single point of responsibility for managing how a project should be built. Developers are sometimes tempted to add .project files and the /libs folder to source control (hereafter referred to as SCM—Source Control Management). They argue that it is a nice convenience to help developers get up to speed quickly. This practice is to be avoided for two reasons:

  1. The POM should be the central point of contact for how to build and manage your project. IDE project configurations—such as .project, .idea, and so on—have their own mechanisms for building and dependency management. If you add these files to SCM, you are effectively duplicating the build/dependency functionality of Maven and will have to consolidate them manually with your POM at regular intervals.
  2. By checking in .project files, you restrict current and future developers to the same IDE and version as yourself.

As a rule, do check in the following:

  • pom.xml
  • src/*.*
  • settings.xml (if any)

Do NOT check in:

  • .project, .settings, .actionScriptProperties, .flexProperties, .idea
  • /libs

ABSOLUTELY NEVER check in:

  • /bin-debug, /bin-release
  • /target

Tip: if you want to share your builds (both releases and snapshots) with other developers, use Nexus, that is what it is for. I'll cover Nexus in depth in the final article.

Note: There are occasions when checking in the libs folder can be appropriate. Typically all your dependencies should be hosted or proxied via an internal Nexus install (covered in detail in the next article). There are occasions, however, when hosting a repository is out of scope. For example, when I put example projects up on Github, I check in third-party SWC dependencies and a brief note on using install:install-file to help developers new to Maven get started running the build.

Finding dependencies

One thing I touched on briefly in Part 1 was how to find dependencies for your POMs. Almost everything you can and will build depends on third-party libraries. The Flex compiler and framework are both simply dependencies. Luckily, many of the most common dependencies are hosted on remote repositories.

To demonstrate the process of finding a dependency, I will use an example that will be useful later on - finding a specific unit testing framework.

What I want is flexunit 4.1.0-8—the same that is bundled with Flash Builder 4.5.1.

Step 1: Maven Central

First off, I check Maven Central via search.maven.org. Recall that Maven Central is defined in the super POM and, if I find it in here, I won't need to add any extra repository information to my POM or settings file. I enter flexunit into the search box and it yields the three items shown in Figure 2:

Figure 2. Searching Maven Central
Figure 2. Searching Maven Central

I can see from this that I've found asmock-flexunit1 and the two Flexmojos unit testing projects—one for FlexUnit and the other for FlexUnit4 . These are projects that Flexmojos requires to run these unit testing frameworks, not the dependency itself. Clearly, none of these satisfies my need, so I'll need to look elsewhere.

Step 2: Sonatype

If the dependency can't be found in Maven Central, the next step is to check the Sonatype repository: repository.sonatype.org. Recall from Part 1 that within this repository are all of the Flex SDK and compiler artifacts—it's the repository that I'm referencing for my Flex builds, so looking in here is the next best bet (see Figure 3).

Note: The Flex SDK and compiler artifacts have been added to the Sonatype "flexgroup" section by the community. Currently, Adobe does not deploy its artifacts to a publicly accessible repository. This is why new versions of the Flex SDK may not appear instantly in the Sonatype repository. The hope is that one day Adobe will deploy its releases to Maven Central as soon as they are published.

Figure 3. Searching Sonatype
Figure 3. Searching Sonatype

I can see from Figure 3 that yes, there is a FlexUnit4 dependency; however, it is not the version I require.

Step 3: The Internet

Well, now I've got to try my luck on the web. It's not in Maven Central or Sonatype, so I could try to see if there is another repository elsewhere hosting my dependency. There are repositories available for Java and Google, but they are unlikely to help me.

Last alternative: Install it internally

At this point, my best solution is to install the dependency into my local repository (or better yet, install it into my internal Nexus instance—but I'll cover that in the next article). This means I need to find the SWC I require and run the install-file goal. In this instance, FlexUnit 4.1.0.8 is available within my Flash Builder install via:

Adobe Flash Builder 4.5/eclipse/plugins/com.adobe.flexbuilder.flexunit_4.5.1.313231/flexunitframework/libs/version4libs/FlexProject/flexunit-4.1.0-8-flex_4.1.0.16076.swc

Note: This build of FlexUnit is built against Flex 4.1, but is satisfactory for my purposes.

So, as in the previous article, I install the file to my local repository. I know from checking Sonatype in the previous step that I should follow the following convention:

groupId: com.adobe.flexunit artifactId: flexunit

Tip: If I wasn't able to find the groupId and artifactId, then, in the worst case, I could have simply made them up. It is an internal dependency, after all; just remember that consistency is the key.

So, from the location of the SWC I want to install, I run:

mvn install:install-file -Dfile=flexunit-4.1.0-8-flex_4.1.0.16076.swc -DgroupId=com.adobe.flexunit -DartifactId=flexunit -Dversion=4.1.0-8 -Dpackaging=swc

Then I can safely add the following dependency to my POM:

<dependency> <groupId>com.adobe.flexunit</groupId> <artifactId>flexunit</artifactId> <version>4.1.0-8</version> <type>swc</type> <scope>test</scope> </dependency>

Truth be told, your organization or institution should have its own install of Nexus, and the dependencies can be added to the third-party repository within it. That way you share these dependencies to all developers on the project. I'll cover this in the next article of this series.

Customizing Flexmojos

Every release of Flexmojos produces a site report that lists usage of the plug-in and the list of goals and configuration settings.

To see the latest plug-in info, check the docs for 4.0-RC2.

Within your POM, the <configuration> section actually aggregates all of the parameters for the plug-in. An example might be:

<project> ... <build> <plugins> <plugin> <groupId>org.sonatype.flexmojos</groupId> <artifactId>flexmojos-maven-plugin</artifactId> <version>4.0-RC2</version> <extensions>true</extensions> <configuration> <compilerWarnings> <warn-no-constructor>false</warn-no-constructor> </compilerWarnings> <debug>false<debug> <coverage>true</coverage> <aggregate>true<aggregate> </configuration> </plugin> </plugins> </build> ... </project>

These correspond to:

  1. From goals compile-swf , compile-swc and asdoc :
  • compilerWarnings
  • debug
  1. From goal test-run :
  • coverage
  1. From goal asdoc (to generate ASDoc files):
  • aggregate

Tip: As you might have guessed, the order of the parameters is irrelevant.

In the previous article, I covered the phases within the SWF and SWC lifecycles (repeated below in Figure 4). As such, if I were to run mvn clean install on, say, a SWF packaged project, I know that the plug-in goals compile-swf, test-compile and test-run will execute. Thus the configuration settings debug , coverage , and compilerWarnings will be read; aggregate is irrelevant for the default lifecycle and will be ignored. If however we were to run mvn flexmojos:asdoc then the parameters debug and compilerWarnings would still apply (as they also exist as parameters within the asdoc goal), as well as aggregate; coverage, on the other hand, would be ignored.

Note: Instead of aggregating all configuration together, and for greater control, you can customize the plug-in itself. The <executions> setting allows configuration of the phases themselves—the goals within them and the configuration for each. This is out of scope for this article series, but for an introduction into the usage of custom executions, check out Introduction to the Build Lifecycle from the Maven docs.

Unit testing

Out of the box, Flexmojos supports automated unit tests as part of the build process. Flexmojos produces Surefire reports that are placed within the /target/surefire-reports folder. By convention, if any unit tests fail or have runtime errors, the build itself will fail.

Recall from Part 1 that the Maven build lifecycle conducts tests after a standard compilation. SWF, SWC, and AIR builds all perform standard unit tests (see Figure 4).

Figure 4. Default SWF lifecycle
Figure 4. Default SWF lifecycle

In order to kick the process off, you must:

  1. Supply a <testSourceDirectory> to your POM. This should point to a folder that hosts your unit tests; typically, it is src/test/flex.
  2. Add your unit testing dependencies (for example, FlexUnit , FlexUnit4, or asunit ).
  3. Make sure Flexmojos can find a standalone Flash Player on your PATH.

I'm going to outline these three in detail. To follow along, check the example project on Github.

Step 1: Setup

Every time you build, Flexmojos will attempt to compile and run any unit tests (matching Test*.as and *Test.as by default) it finds and execute them. You can customize this matching pattern using the configuration setting includeTestFiles (see the "Test configuration settings" section).

You need to ensure you have a folder for your tests. The general rule is src/test/flex, but as mentioned before, it is up to you.

Inside that folder or any subfolder, you need at least one class beginning or ending in "Test" that has unit tests. As I'm using the example from Github, I'll use a FlexUnit4 test:

public class TestRandomCity { protected var cities:RandomCity; [Before] public function setUp():void { cities = new RandomCity(); } [Test] public function testGetNext():void { for (var i:int = 1; i < 100; i++) assertThat(cities.next); } [After] public function tearDown():void { cities = null; } }

Step 2: Test Dependencies

From the earlier section, recall that the FlexUnit 4.0-rc-1 dependency is hosted on Sonatype. As such, I could simply use that version, but as I installed the 4.1.0-8 version into my repository earlier, I'd rather use:

<dependency> <groupId>com.adobe.flexunit</groupId> <artifactId>flexunit</artifactId> <version>4.1.0-8</version> <type>swc</type> <scope>test</scope> </dependency>

Notice anything different? I've added the parameter <scope> of test . This ensures that the dependency will only be used when compiling unit tests, not when compiling the artifact itself. The next and final article in the series will cover scope in more depth.

If FlexUnit4 isn't your cup of tea, never fear: Flexmojos supports a plethora of unit testing frameworks. Checkout the Flexmojos project on Github for some unit testing examples using various frameworks.

You may also find yourself requiring toolsets like fluint, hamcrest-as3 or mockolate. Follow the process for finding dependencies from the "Finding dependencies" section. More often than not, you'll find yourself having to install the SWCs locally.

Step 3: Finding Flash Player

To run the unit tests from Maven, Flexmojos needs to find the Flash Player executable. You must make sure Flash Player (Flash Player on OS X, flashplayer on Linux, and Flashplayer.exe on Windows) is available on your PATH. You can download the stand-alone Flash Player Projector from Adobe.

In Windows, unpack the download and add that location to your PATH via the My Computer > Advanced > Environment Variables command. On OS X, I moved the downloaded Flash Player.app to usr/local/bin and added the following to my ~/.bash_profile:

export FLASH_PLAYER='/usr/local/bin/Flash Player.app/Contents/MacOS' export PATH=${M2_HOME}/bin:$FLASH_PLAYER:${PATH}

If you're still stuck, here are some additional instructions from Flexmojos.

Configuring the tests

As mentioned earlier, the configuration of your POM combines settings from various goals. Regarding unit tests, you have two goals to work with:

  • test-compile goal
  • test-run goal

In those links, you'll find collections of parameters that you can use to customize how your project should compile the unit tests and how they should run.

For example, if you want to modify the pattern matching of unit tests, you could apply the following to your POM (inside the Flexmojos plug-in section):

<configuration> <includeTestFiles> <includeTestFile>*Tests.as</includeTestFile> </includeTestFiles> </configuration>

Tip: If you want to skip tests in your build at any time, simply use -DskipTests as in mvn clean install -DskipTests .

Running tests on a build server

Automating unit tests on a Linux build server, which typically has no GUI (and as such is headless), requires a bit more effort. Currently, Flexmojos uses Xvfb, a framebuffer tool, to emulate the GUI for Flash Player. For more information, check out this post from Flexmojos:

Tips for flexmojos + Linux + continuous integration

Code coverage

Want to see how much of your code is covered by your unit tests? Flexmojos 4 includes the generation of code coverage reports in Cobertura. It uses Apparat (written in Scala) to produce coverage reports (and incidentally, also for optimization).

<configuration> ... <coverage>true</coverage> <coverageProvider>cobertura</coverageProvider> <coverageReportFormat>html</coverageReportFormat> ... </configuration>

This outputs a collection of HTML files with line-by-line code coverage under /target/coverage. If you'd rather integrate it into your build server, switch the <coverageReportFormat> parameter to xml .

Tip: If you're using Hudson or Jenkins, add the Cobertura plug-in and use the following XML report pattern to aggregate all of your Maven modules' coverage data: **/target/coverage/coverage.xml .

Adding projects

Soon after finding your feet in Maven, it's time to build a real-world project. As such, a simple application with a few dependencies is hardly going to cut the mustard.

You can find the code to this example on Github.

As you add projects to your application—such as libraries and runtime modules—you will need to create POMs for each of them. The typical process is to:

  1. Create a parent POM that contains all settings and configuration to share across the application.
  2. Create directories for each project within your application.
  3. Within each project, create POM and add source files.

An example might look like:

Figure 5. Project with multiple POMs
Figure 5. Project with multiple POMs

Two mechanisms in Maven let you reference one POM in another: inheritance and aggregation.

Inheritance

As mentioned earlier, all POMs inherit from the Maven super POM. This provides a collection of settings for each execution, including the repository location of Maven Central.

If you want to create a child POM that inherits from a parent, do the following:

  1. Create a parent POM with a <packaging> parameter of pom , enabling it to be used as a superclass for inheritance. Any configuration in this POM will be shared among all children:
<project> <modelVersion>4.0.0</modelVersion> <groupId>org.justinjmoses.flexmojos-introduction.libraries-example</groupId> <artifactId>parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> </project>
  1. Create a child POM that inherits from the parent:
<project> <parent> <groupId>org.justinjmoses.flexmojos-introduction.libraries-example</groupId> <artifactId>parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>library</artifactId> <packaging>swc</packaging> </project>

Note: the child POM still needs to refer to the parent's version number. It can inherit this version for itself, however (and the groupId , for that matter).

Tip: Another Maven convention is at work here. The relative URL of the parent POM is assumed to be ../pom.xml. You can override this with the <relativeUrl> parameter within the <parent> element.

Now you can individually build any of the child POMs and be assured that they will inherit their settings from the one parent. So, what if you want to build all at once? That's where aggregation comes in.

Aggregation

To process other POMs from within a POM, you need to create definitions of the location of these other POMs. These projects are referred to as modules in Maven.

As such, you can add the following modules to your project, and it becomes an aggregator project.

<project> <modelVersion>4.0.0</modelVersion> <groupId>org.justinjmoses.flexmojos-introduction.libraries-example</groupId> <artifactId>parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>application</module> <module>library</module> </modules> </project>

Note: As with inheritance, the <packaging> parameter must be set to pom to perform aggregation.

Tip: The actual module name corresponds to the relative folder of the module, not the artifact inside the module. Typically, modules live directly off the aggregator project. Alternate locations are valid, however: for example, <module>../../my-project</module>

As part of the aggregation process, Maven ensures that it determines what interdependencies—if any—there are between the modules and constructs a build order to match.

For example, when you add the following dependency to the application (SWF) module:

<dependency> <groupId>org.justinjmoses.flexmojos-introduction.libraries-example</groupId> <artifactId>library</artifactId> <version>1.0.0-SNAPSHOT<version> <type>swc</type> </dependency>

Maven will determine that the library (SWC) module must be built first in order for the application (SWF) to start.

Note: Don't confuse Maven modules with Flex runtime modules—they are completely different.

Combining inheritance with aggregation

With each child POM inheriting from the parent POM and the parent POM aggregating the child modules, the project is now integrated, and you can kick off the build from the parent and put any common configuration into the parent POM.

Most of the time, your projects will combine inheritance and aggregation. Nevertheless, there may be instances where you do not want to use both. Aggregating simply allows you to start processing multiple POMs, regardless of their relationship to the currently executing POM. Inheritance, on the other hand, lets you share and override settings from a parent to a child.

With both tools employed, you can update your interdependency in the application (SWF) module to:

<dependency> <groupId>${project.groupId}</groupId> <artifactId>library</artifactId> <version>${project.version}<version> <type>swc</type> </dependency>

The ${project.groupId} and ${project.version} settings are shared across all of the child POMs via inheritance, so it's a convenience to not have to explicitly state the interdependencies within the child POMs.

Where to go from here

This article has covered project structure, setup, and archetypes; a workflow for finding dependencies; customizing the various goals within Flexmojos; automated unit tests and code coverage, and finally, adding additional projects to your build with inheritance and aggregation.

The final article in this series talks about dependency scope and discuss the usage of RSLs, runtime Flex modules, WAR packaging, build profiling and ASDoc support.

Here are some useful resources:

  • The latest version of Maven
  • Flexmojos homepage
  • Flexmojos 4.0-RC2 plug-in documentation
  • Follow Flexmojos on Twitter
  • Flexmojos community help
  • My example projects
  • Comprehensive list of many common (albeit Java) plug-ins
  • Definitive guide to Maven and Flexmojos

Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License

More Like This

  • Flex and Maven with Flexmojos – Part 3: Journeyman
  • Flex and Maven with Flexmojos – Part 1: First steps
  • Data push from .NET to Flex with MSMQ
  • Deploying a Flex application with ColdFusion URL variables

Tutorials & Samples

Tutorials

  • Flex mobile performance checklist
  • Flex and Maven with Flexmojos – Part 3: Journeyman
  • Migrating Flex 3 applications to Flex 4.5 – Part 4

Samples

  • Twitter Trends
  • Flex 4.5 reference applications
  • Mobile Trader Flex app on Android Market

Flex User Forum

More
07/25/2011 Flash Player Debug Issues - Safari 5.1 & Chrome 13
04/22/2012 Loader png - wrong color values in BitmapData
04/22/2012 HTTPService and crossdomain.xml doesn't work as expected
04/23/2012 Memory related crashes in Flex application

Flex Cookbook

More
04/06/2012 How to detect screen resize with a SkinnableComponent
02/29/2012 Embed Stage3D content inside Flex application components
02/15/2012 Custom WorkFlow Component
02/09/2012 Using Camera with a MediaContainer instead of VideoDisplay

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement