45
Optimizing Flex Applications David Coletta Virtual Ubiquity, Inc. [email protected] Blog: http://www.colettas.org

Optimizing Flex Applications

  • Upload
    dcoletta

  • View
    46.663

  • Download
    1

Embed Size (px)

DESCRIPTION

Slides from my Adobe MAX 2007 talk, "Optimizing Flex Applications"

Citation preview

Page 1: Optimizing Flex Applications

Optimizing Flex ApplicationsOptimizing Flex Applications

David ColettaVirtual Ubiquity, [email protected]: http://www.colettas.org

David ColettaVirtual Ubiquity, [email protected]: http://www.colettas.org

Page 2: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

IntroductionIntroduction

Developer and co-founder at Virtual Ubiquity

Career focus on collaboration software Background in C++ and web applications Don’t know much about optimization

Developer and co-founder at Virtual Ubiquity

Career focus on collaboration software Background in C++ and web applications Don’t know much about optimization

Page 3: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Structure of this talkStructure of this talk

Taxonomy of optimization Best practices Flex 3 Profiler Case studies Questions

Taxonomy of optimization Best practices Flex 3 Profiler Case studies Questions

Page 4: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Taxonomy of optimizationTaxonomy of optimization Improving actual performance Improving perceived

performance

Improving actual performance Improving perceived

performance

Page 5: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Improving actual performanceImproving actual performance

Expensive algorithm: find a cheaper one Precompute things that can be

precomputed Identify and refactor superfluous code Reduce load on GC by plugging memory

leaks, allocating fewer objects, etc.

Expensive algorithm: find a cheaper one Precompute things that can be

precomputed Identify and refactor superfluous code Reduce load on GC by plugging memory

leaks, allocating fewer objects, etc.

Page 6: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Improving actual performanceImproving actual performance

Verify build configuration (optimization should be on)

Reduce functionality (e.g., turn down suggestions on spell checker)

Take advantage of what the platform does well, avoid what it doesn't

Verify build configuration (optimization should be on)

Reduce functionality (e.g., turn down suggestions on spell checker)

Take advantage of what the platform does well, avoid what it doesn't

Page 7: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Improving perceived performanceImproving perceived performance

Doing too much work up front; do some of it later

Move lengthy operations into the background

Show progress during extended operations

Doing too much work up front; do some of it later

Move lengthy operations into the background

Show progress during extended operations

Page 8: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Too Much For One Talk!Too Much For One Talk!

Expensive algorithm: find a cheaper one Precompute things that can be precomputed Identify and refactor superfluous code Reduce load on GC by plugging memory leaks, allocating

fewer objects, etc. Verify build configuration (optimization should be on) Reduce functionality (e.g., turn down suggestions on spell

checker)

Take advantage of what the platform does well, avoid what it doesn't

Doing too much work up front; do some of it later Move lengthy operations into the background Show progress during extended operations

Expensive algorithm: find a cheaper one Precompute things that can be precomputed Identify and refactor superfluous code Reduce load on GC by plugging memory leaks, allocating

fewer objects, etc. Verify build configuration (optimization should be on) Reduce functionality (e.g., turn down suggestions on spell

checker)

Take advantage of what the platform does well, avoid what it doesn't

Doing too much work up front; do some of it later Move lengthy operations into the background Show progress during extended operations

Page 9: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Big PictureBig PictureRendering-intensive tasks(effects, scrolling, resizing)Rendering-intensive tasks(effects, scrolling, resizing)

ActionScript

Rendering

Other

Other tasks (startup, navigation, data manipulation)

Critical areas:Object creation Measurement/Layout Rendering

Page 10: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Optimizing Actionscript: Object Creation

Optimizing Actionscript: Object Creation

Page 11: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

The Birth of an ObjectThe Birth of an Object

Create instance of ActionScript class Assign initial property values

<mx:TextArea text=“Hi” width=“100”/> Wire objects together

Add new object to display list Event handlers: <mx:Button click=“goNext()”/> Data binding: <mx:Label text=“{city}”/> Effect listeners: <mx:Label

showEffect=“{fade}”/>

Create instance of ActionScript class Assign initial property values

<mx:TextArea text=“Hi” width=“100”/> Wire objects together

Add new object to display list Event handlers: <mx:Button click=“goNext()”/> Data binding: <mx:Label text=“{city}”/> Effect listeners: <mx:Label

showEffect=“{fade}”/>

Page 12: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Solution #1: Deferred CreationSolution #1: Deferred Creation

Delay object creation until the object becomes visible

Is baked into Accordion, TabNavigator, and ViewStack

Can be added to custom containers, but subclassing ViewStack is easier

Delay object creation until the object becomes visible

Is baked into Accordion, TabNavigator, and ViewStack

Can be added to custom containers, but subclassing ViewStack is easier

Page 13: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Solution #2: Ordered CreationSolution #2: Ordered Creation During startup, stagger creation of

objects

Improves perceived startup time

During startup, stagger creation of objects

Improves perceived startup time<mx:Application>

<mx:Panel width="250" height="100" creationPolicy=“queued” />

<mx:Label text="One" /> </mx:Panel>

<mx:Panel width="250" height="100" creationPolicy=“queued” /> <mx:Label text="Two" /> </mx:Panel>

</mx:Application>

Page 14: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Solution #3: Use <mx:Repeater> CarefullySolution #3: Use <mx:Repeater> Carefully Don’t allow <mx:Repeater> to create elements that are clipped

Bad:

Good:

Caveat: Repeater scrolls more smoothly

Don’t allow <mx:Repeater> to create elements that are clipped

Bad:

Good:

Caveat: Repeater scrolls more smoothly

<mx:VBox> <mx:Repeater id=“r” dataProvider=“{arr}”> <mx:Image source=“r.currentItem.url”/> </mx:Repeater></mx:VBox>

<mx:List dataProvider=“{arr}”> <mx:itemRenderer> <mx:Component> <mx:Image source=“{dataObject.url}”/> </mx:Component> </mx:itemRenderer></mx:List>

Page 15: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Optimizing Actionscript: Measurement/Layout

Optimizing Actionscript: Measurement/Layout

Page 16: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Measurement/Layout: DefinitionMeasurement/Layout: DefinitionThe process of assigning a position and size to every componentThe process of assigning a position and size to every component

<mx:Application> <mx:HBox> <mx:Button label=“1”/> <mx:Button label=“2”/> </mx:HBox> <mx:TextArea width=“100%” height=“100%” text=“Text”/> </mx:Application>

Page 17: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Measurement/Layout: DescriptionMeasurement/Layout: Description

Measurement Phase: traverse tree from bottom up Buttons and TextArea compute measured sizes HBox computes its measured size Application computes its measured size

Layout Phase: traverse tree from top down Application sets sizes and positions of HBox and TextArea HBox sets sizes and positions of Buttons

O(n) algorithm, n = number of objects

Measurement Phase: traverse tree from bottom up Buttons and TextArea compute measured sizes HBox computes its measured size Application computes its measured size

Layout Phase: traverse tree from top down Application sets sizes and positions of HBox and TextArea HBox sets sizes and positions of Buttons

O(n) algorithm, n = number of objects

<mx:Application> <mx:HBox> <mx:Button label=“1”/> <mx:Button label=“2”/> </mx:HBox> <mx:TextArea width=“100%” height=“100%” text=“Text”/>

</mx:Application>

Page 18: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Solution #1: Reduce Container NestingSolution #1: Reduce Container Nesting

Try to use HBox and VBox instead of Grid Avoid nesting a VBox inside a Panel or

Application The root of an MXML component doesn’t

need to be a Container Use Canvas with constraints Warning sign: a Container with a single

child

Try to use HBox and VBox instead of Grid Avoid nesting a VBox inside a Panel or

Application The root of an MXML component doesn’t

need to be a Container Use Canvas with constraints Warning sign: a Container with a single

child

Page 19: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Solution #2: Avoid Redundant Measurement/LayoutSolution #2: Avoid Redundant Measurement/Layout

Scenario: Flickr app issues 25 image requests When image data arrives, corresponding Image object resizes For each image, whole screen does measurement/layout

Scenario: Dashboard app creates 6 portal windows Each portal issues web service request For each web service response, portal window’s size changes

Solutions: Delay requests until creationComplete (after incremental

layout) Limit geometry changes when responses arrives Stagger requests or queue responses

Scenario: Flickr app issues 25 image requests When image data arrives, corresponding Image object resizes For each image, whole screen does measurement/layout

Scenario: Dashboard app creates 6 portal windows Each portal issues web service request For each web service response, portal window’s size changes

Solutions: Delay requests until creationComplete (after incremental

layout) Limit geometry changes when responses arrives Stagger requests or queue responses

Page 20: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Optimizing RenderingOptimizing Rendering

Page 21: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Redraw RegionsRedraw Regions If an object's properties are changed, its

bounding box is a “redraw region” Visualize using “show redraw region”

Debug player only

Objects that overlap the redraw region are redrawn

If an object's properties are changed, its bounding box is a “redraw region”

Visualize using “show redraw region” Debug player only

Objects that overlap the redraw region are redrawn

Page 22: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

cacheAsBitmap Protects Innocent BystanderscacheAsBitmap Protects Innocent Bystanders

If cacheAsBitmap is true thenthe object and its children are rendered into an offscreen bitmap

If an object overlaps a redraw region and the object is unchanged then the cached bitmap is used

Example: a Move effect

If cacheAsBitmap is true thenthe object and its children are rendered into an offscreen bitmap

If an object overlaps a redraw region and the object is unchanged then the cached bitmap is used

Example: a Move effect

Page 23: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

cacheAsBitmap is a Double-Edged SwordcacheAsBitmap is a Double-Edged Sword

Objects with cached bitmaps are more expensive to change

Examples when cacheAsBitmap hurts performance Resize effect Resizing the browser window

Suggestion: cache bitmaps only for short periods of time

Objects with cached bitmaps are more expensive to change

Examples when cacheAsBitmap hurts performance Resize effect Resizing the browser window

Suggestion: cache bitmaps only for short periods of time

Page 24: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Factors that Affect Rendering SpeedFactors that Affect Rendering Speed

Size of redraw region Suggestion: refactor UI

Cached bitmaps (can help or hurt) Total number of vectors in the redraw region

Suggestion: simplify geometry Suggestion: use Resize.hideChildren and hide children during

state transition

Mixture of device text and vector graphics Clip masks Filters (e.g.: DropShadow) Other background processing

Suggestion: Effect.suspendBackgroundProcessing

Size of redraw region Suggestion: refactor UI

Cached bitmaps (can help or hurt) Total number of vectors in the redraw region

Suggestion: simplify geometry Suggestion: use Resize.hideChildren and hide children during

state transition

Mixture of device text and vector graphics Clip masks Filters (e.g.: DropShadow) Other background processing

Suggestion: Effect.suspendBackgroundProcessing

Page 25: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Miscellaneous OptimizationsMiscellaneous Optimizations

Page 26: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Reducing Memory UsageReducing Memory Usage

Discard unused UI myViewStack.removeChild(childView); childView.removeEventListener(“click”,

clickHandler) or use weak references

Clear references to unused data myProperty = null; myWebService.getAddressBook.clearResult(

); Use memory profiling tools

Discard unused UI myViewStack.removeChild(childView); childView.removeEventListener(“click”,

clickHandler) or use weak references

Clear references to unused data myProperty = null; myWebService.getAddressBook.clearResult(

); Use memory profiling tools

Page 27: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Setting StylesSetting Styles Changing a rule set is most expensive

StyleManager.styles.Button.setStyle(“color”, 0xFF0000)

For inline styles, expense is proportional to the number of objects affected Example: myVBox.setStyle(“color”, 0xFF0000) Exception: setStyle is cheap during object creation

If a value will change at runtime, initialize it at authoring time <mx:Style> Button { color: #000000 }

</mx:Style> <mx:VBox id=“myVBox” color=“0x000000”>

Changing a rule set is most expensive StyleManager.styles.Button.setStyle(“color”,

0xFF0000) For inline styles, expense is proportional to

the number of objects affected Example: myVBox.setStyle(“color”, 0xFF0000) Exception: setStyle is cheap during object creation

If a value will change at runtime, initialize it at authoring time <mx:Style> Button { color: #000000 }

</mx:Style> <mx:VBox id=“myVBox” color=“0x000000”>

Page 28: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Flex 3 ProfilerFlex 3 Profiler

Lets you measure: Call frequency Method duration Call stacks Number of instances of objects Object size Garbage collection

Lets you measure: Call frequency Method duration Call stacks Number of instances of objects Object size Garbage collection

Page 29: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

How the Profiler WorksHow the Profiler Works

Uses new Player APIs 10 ms sampling interval Computes cumulative values Records internal Player actions (e.g.,

[keyboardEvent], [mark], [sweep])

Uses new Player APIs 10 ms sampling interval Computes cumulative values Records internal Player actions (e.g.,

[keyboardEvent], [mark], [sweep])

Page 30: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Two Kinds of ProfilingTwo Kinds of Profiling

Performance profiling Looking for slow code Find slow methods and speed them up Find frequently called methods and reduce

frequency

Memory profiling Looking for excessive memory consumption Find big objects and make them smaller Find numerous objects and make fewer of them

Performance profiling Looking for slow code Find slow methods and speed them up Find frequently called methods and reduce

frequency

Memory profiling Looking for excessive memory consumption Find big objects and make them smaller Find numerous objects and make fewer of them

Page 31: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Profiler ScenarioProfiler Scenario

Problem: Document organizer is slow to redraw after deleting a document

Tasks Measure (redraw operation) Identify (slow code) Fix (rewrite, reorganize, remove)

Problem: Document organizer is slow to redraw after deleting a document

Tasks Measure (redraw operation) Identify (slow code) Fix (rewrite, reorganize, remove)

Page 32: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Profiler DemoProfiler Demo

Page 33: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Case Study: Activa Live ChatCase Study: Activa Live ChatProvided by the team at Activa Live Chat

http://activalive.com

List item renderers Reference counting

Provided by the team at Activa Live Chat

http://activalive.com

List item renderers Reference counting

Page 34: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Aptiva Live Chat ScreencastAptiva Live Chat Screencast

Page 35: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

MXML ContainersMXML Containers

Complex layout engine Clipping Dynamic Instantiation Scrolling Borders Styling Engine

Complex layout engine Clipping Dynamic Instantiation Scrolling Borders Styling Engine

Page 36: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

MXML Item RendererMXML Item Renderer

Page 37: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Manual LayoutManual Layout

More Complex Difficult to style for non-coding

designers Better Performance

More Complex Difficult to style for non-coding

designers Better Performance

Page 38: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Custom Item RendererCustom Item Renderer

Page 39: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Garbage CollectorGarbage Collector

Every reference to an object increases the reference count

Deleting references to an object decrements the reference count

Objects with a positive reference count will not be collected

Inattention leads to memory leaks

Every reference to an object increases the reference count

Deleting references to an object decrements the reference count

Objects with a positive reference count will not be collected

Inattention leads to memory leaks

Page 40: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Reference CountingReference Counting

Event Listeners, by default, increment the reference counter

useWeakReference = no increase in reference count

Good Practice = Remove Event Listeners

Event Listeners, by default, increment the reference counter

useWeakReference = no increase in reference count

Good Practice = Remove Event Listeners

Page 41: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Case Study: eBay SDKCase Study: eBay SDK

Provided by Adam Flater, Software Architect, EffectiveUI

Provided by Adam Flater, Software Architect, EffectiveUI

Page 42: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

WebWatcher ScreencastWebWatcher Screencast

QuickTime™ and aAnimation decompressor

are needed to see this picture.

Page 43: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Goal: represent hierarchical data from web service as objects in Flex

Goal: represent hierarchical data from web service as objects in Flex Point the Axis wsdl2java at the eBay

wsdl to generate a bunch of data classes

Use a custom java class to translate the java data classes to action script classes (using java introspection)

Write serializers / deserializers in AS to translate the data objects between San Dimas and the web service

Point the Axis wsdl2java at the eBay wsdl to generate a bunch of data classes

Use a custom java class to translate the java data classes to action script classes (using java introspection)

Write serializers / deserializers in AS to translate the data objects between San Dimas and the web service

Page 44: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Optimizing IntrospectionOptimizing Introspection

For serialization, used introspection initially, but recursion is very costly

Instead, did this:

For serialization, used introspection initially, but recursion is very costly

Instead, did this:

Page 45: Optimizing Flex Applications

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

David Coletta, Virtual Ubiquity, Inc.Blog: http://www.colettas.org

Questions?Questions?