demetrio812

13 October 2007

Creating a JSF component using RichFaces CDK

Hi,
this time I will explain how to create a simple output component using the Ajax4Jsf/Richfaces CDK framework.

CDK is a framework that simplifies the JSF component creation process so you can concentrate on developing the component functionalities instead of thinking to all the boiler code that this kind of work need.

There isn't an official manual on how to use it (it's still on development) so you have to look on richfaces forum and wiki, and some article you can find on net (see the links at the end of this post).

Before starting I want to point that I'm learning this stuff too and I'm not an expert on jsf component making and I'm still learning. Every suggestion is welcome.

Also you have to assure to have maven installed and working on your system, you have also to configure it for Ajax4Jsf/RichFaces CDK as it is written here.

We are going to make a skypecall component: it insert a link that permit to call a person from the web page via skype.

Let's start!

First of all create a new directory to start from (use a short and without space path);

After that create a file named pom.xml and put this content (modify values with your data if you want):

<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>it.novaware</groupId>
<artifactId>ui</artifactId>
<url>http://www.novaware.it</url>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Novaware JSF components library</name>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<modules>

</modules>
</project>

Now open a shell, go in the root directory and execute this command:

mvn archetype:create -DarchetypeGroupId=org.richfaces.cdk -DarchetypeArtifactId=maven-archetype-jsf-component -DarchetypeVersion=3.1.5-GA -DgroupId=it.novaware -DartifactId=skypecall
with skypecall as the name of your component.

In the new generated directory there is a pom.xml file. Open it and add this code after the </build> tag:

<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.richfaces.framework</groupid>
<artifactid>richfaces-impl</artifactid>
<version>3.1.1-GA</version>
</dependency>
</dependencies>

Using the shell, enter in the new directory (cd skypecall).

Execute this command to create the new component structure:
mvn cdk:create -Dname=skypecall
Now you can try it compiling it and making the jar:
mvn install

(To clean the build you can execute: mvn clean)

Mvn install must say BUILD SUCCESSFUL.

Now we are ready to personalize the new component!

Go in the generated src/main directory of skypecall directory: you will find 4 new subfolders with generated files.

Let's start from the config: edit the skypecall/src/main/config/component/skypecall.xml file to insert the component attributes. We are going to insert 5 attributes:

  1. img: the kind of image to insert. You can use the "default" value to show the default image (see color and size attributes),
    the "none" value to show no image, or directly put the URL to show the image you like. So possible value are: default, none, url_of_image. Default is "default".

  2. color: the color of the default image. It works if "image" attribute is "default". It can be "green" or "blue". Default is "blue".

  3. size: the size of the default image. It works if "image" attribute is "default". Possible values are: small, medium, big. Default is "medium".

  4. number: skype number to call. Default is null.

  5. type: the type of skype link to create. Possible values are: call, chat, voicemail, add, userinfo, sendfile. Default is call.

So the final code of skypecall.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//AJAX4JSF//CDK Generator config/EN" "https://ajax4jsf.dev.java.net/nonav/dtds/component-config.dtd" >
<components>
<component>
<name>org.richfaces.Skypecall</name>
<family>org.richfaces.Skypecall</family>
<classname>org.richfaces.component.html.HtmlSkypecall</classname>
<superclass>org.richfaces.component.UISkypecall</superclass>
<description>
<![CDATA[
A component that insert a link that permit to call a person from the web via skype.
]]>
</description>
<renderer generate="true" override="true">
<name>org.richfaces.SkypecallRenderer</name>
<template>org/richfaces/htmlSkypecall.jspx</template>
</renderer>
<tag>
<name>skypecall</name>
<classname>org.richfaces.taglib.SkypecallTag</classname>
<superclass>
org.ajax4jsf.webapp.taglib.HtmlComponentTagBase
</superclass>
</tag>
<!--
<taghandler>
<classname>org.ajax4jsf.tag.TestHandler</classname>
</taghandler>
-->
&ui_component_attributes;
&html_events;
&html_style_attributes;
<property>
<name>img</name>
<classname>java.lang.String</classname>
<description>The kind of image to insert. You can use the "default" value to show the default image (see color and size attributes),
the "none" value to show no image, or directly put the URL to show the image you like. So possible value are: default, none, url_of_image. Default is "default".
</description>
<defaultvalue>"default"</defaultvalue>
</property>
<property>
<name>color</name>
<classname>java.lang.String</classname>
<description>The color of the default image. It works if "image" attribute is "default". It can be "green" or "blue". Default is "blue".
</description>
<defaultvalue>"blue"</defaultvalue>
</property>
<property>
<name>size</name>
<classname>java.lang.String</classname>
<description>The size of the default image. It works if "image" attribute is "default". Possible values are: small, medium, big. Default is "medium".
</description>
<defaultvalue>"medium"</defaultvalue>
</property>
<property>
<name>number</name>
<classname>java.lang.String</classname>
<description>Skype number to call. Default is null.
</description>
<defaultvalue>""</defaultvalue>
</property>
<property>
<name>type</name>
<classname>java.lang.String</classname>
<description>The type of skype link to create. Possible values are: call, chat, voicemail, add, userinfo, sendfile. Default is call.
</description>
<defaultvalue>"call"</defaultvalue>
</property>
<!--
<property>
<name>param</name>
<classname>java.lang.String</classname>
<description>
</description>
<defaultvalue>"default"</defaultvalue>
</property>
-->
</component>
</components>

Now we have to modify the jspx template that will generate the renderer, edit src/main/templates/it/novaware/htmlSkypecall.jspx, it might look like that:


<?xml version="1.0" encoding="UTF-8"?>
<f:root
xmlns:f="http://ajax4jsf.org/cdk/template"
xmlns:c=" http://java.sun.com/jsf/core"
xmlns:ui=" http://ajax4jsf.org/cdk/ui"
xmlns:u=" http://ajax4jsf.org/cdk/u"
xmlns:x=" http://ajax4jsf.org/cdk/x"
class="it.novaware.renderkit.html.SkypecallRenderer"
baseclass="org.ajax4jsf.renderkit.AjaxComponentRendererBase"
component="it.novaware.component.UISkypecall"
>

<f:clientid var="clientId"/>
<!--
I can't use this because I can't use parameter inside the name attribute
<f:resource var="skypeImage2" name="/images/green-medium.png" />-->

<jsp:scriptlet>
<![CDATA[
/* See what I have to do with internal image */
String skypeImage = null;
String image=(String) component.getAttributes().get("img");

if (image.compareToIgnoreCase("default")==0) {
/* dynamically compose the default image name */
skypeImage = getResource( "images/"+(String)component.getAttributes().get("color")+"-"+(String)component.getAttributes().get("size")+".png" ).getUri(context, component);
} else if (image.compareToIgnoreCase("none")==0) {
/* skypeImage stays null */
} else {
/* put the url of the image */
skypeImage=image;
}

variables.setVariable("skypeImage",skypeImage);
]]>
</jsp:scriptlet>
<a id="#{clientId}"
name="#{clientId}"
x:passThruWithExclusions="value,name,type,id,number,size,color"
href="skype:#{component.attributes['number']}?#{component.attributes['type']}"
>
<jsp:scriptlet>
<![CDATA[
if (variables.getVariable("skypeImage")!=null) {
]]>
</jsp:scriptlet>
<img src="#{skypeImage}" border="0"/>
<jsp:scriptlet>
<![CDATA[
}
]]>
</jsp:scriptlet>
<vcp:body>
<f:call name="renderChildren" />
</vcp:body>
</a>
</f:root>

Notice that I have to point a dynamic named image (it is composed by the color and the size, look at the code) so I can't use the f:resource tag and I have to use a scriptlet.

I also called renderChilder using the f:call tag to be able to put other components inside the skypecall anchor tag.

I think that it is clear what the code do: if the img attribute of the component is none it doesn't put the img tag; if it is default or omitted it uses the color and size attribute to find a default image (that we'll copy in the resource directory in the next final step); if not it uses the passed value as URL for the src attribute of the img tag.

As I said, the final step is to copy the resources images in the appropriate directory so go to skypecall/src/main/resources and insert the it/novaware/renderkit/html/images directories: go inside and copy the default image files that you can find here.

You have finished!

After that you can come back to your shell at skypecallskypecall directory and launch mvn install.

Let's try it!

If you look inside the generatedskypecall/target directory you will find a jar library called skypecall-1.0-SNAPSHOT.jar: you have to copy it in the WEB-INF/libdirectory of your project.

To use it in an xhtml page just use the following namespace:

http://www.novaware.it/skypecall
So if you are using Facelets add:
<xmlns:nw="http://www.novaware.it/skypecall">
If you are using JSP add:
<%@ taglib uri="http://www.novaware.it/skypecall" prefix="nw"%>
to each page that uses the component.

Here some example of use:


<nw:skypecall img="default" color="blue" number="echo123" type="call" size="small" ></nw:skypecall>

<nw:skypecall size="big" color="green" number="echo123" ></nw:skypecall>

<nw:skypecall size="medium" color="green" number="echo123" ></nw:skypecall>

<nw:skypecall img="default" color="green" number="echo123" type="call" size="small" ></nw:skypecall>

<nw:skypecall img="none" number="echo123" type="call" >call me!</nw:skypecall>

That would produce the following:


Use can also download the source files from here and the compiled library from here.

Here some links you can start from:

As I said, every suggestion is welcome!!

Demetrio

Labels:

21 Comments:

  • What was the purpose to put all attributes to the UI class?

    By Blogger Sergey, At October 18, 2007 at 12:41 AM  

  • Hi, I saw the spacer example: there are some (not all) attributes in UI class but I can't see where they are used so I thought to put all...

    Can you explain better this point so I can correct the article?

    Thanks

    Demetrio

    By Blogger demetrio.it, At October 18, 2007 at 12:58 AM  

  • Just for information, the discussion continued here.

    Demetrio

    By Blogger demetrio.it, At October 21, 2007 at 4:59 PM  

  • Hello demetrio.it! I get the error stack below when trying to follow your instructions. Could you please update them, to reflect the current repositories?

    Thanks!

    -----------

    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'archetype'.
    [INFO] Ignoring available plugin update: 2.0-alpha-1 as it requires Maven version 2.0.7
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/1.0-alpha-7/maven-archetype-plugin-1.0-alpha-7.pom
    4K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype/1.0-alpha-7/maven-archetype-1.0-alpha-7.pom
    3K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-parent/2/maven-archetype-parent-2.pom
    2K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/1.0-alpha-7/maven-archetype-plugin-1.0-alpha-7.jar
    12K downloaded
    [INFO] ----------------------------------------------------------------------------
    [INFO] Building Novaware JSF components library
    [INFO] task-segment: [archetype:create] (aggregator-style)
    [INFO] ----------------------------------------------------------------------------
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-core/1.0-alpha-7/maven-archetype-core-1.0-alpha-7.pom
    1K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-downloader/1.0/maven-downloader-1.0.pom
    1K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-creator/1.0-alpha-7/maven-archetype-creator-1.0-alpha-7.pom
    1K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-model/1.0-alpha-7/maven-archetype-model-1.0-alpha-7.pom
    1K downloaded
    Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.0-alpha-5/plexus-archiver-1.0-alpha-5.pom
    439b downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-creator/1.0-alpha-7/maven-archetype-creator-1.0-alpha-7.jar
    23K downloaded
    Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.0-alpha-5/plexus-archiver-1.0-alpha-5.jar
    129K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-model/1.0-alpha-7/maven-archetype-model-1.0-alpha-7.jar
    18K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-core/1.0-alpha-7/maven-archetype-core-1.0-alpha-7.jar
    24K downloaded
    Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-downloader/1.0/maven-downloader-1.0.jar
    6K downloaded
    [INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
    [INFO] Setting property: velocimacro.messages.on => 'false'.
    [INFO] Setting property: resource.loader => 'classpath'.
    [INFO] Setting property: resource.manager.logwhenfound => 'false'.
    [INFO] **************************************************************
    [INFO] Starting Jakarta Velocity v1.4
    [INFO] RuntimeInstance initializing.
    [INFO] Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties
    [INFO] Default ResourceManager initializing. (class org.apache.velocity.runtime.resource.ResourceManagerImpl)
    [INFO] Resource Loader Instantiated: org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader
    [INFO] ClasspathResourceLoader : initialization starting.
    [INFO] ClasspathResourceLoader : initialization complete.
    [INFO] ResourceCache : initialized. (class org.apache.velocity.runtime.resource.ResourceCacheImpl)
    [INFO] Default ResourceManager initialization complete.
    [INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Literal
    [INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Macro
    [INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Parse
    [INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Include
    [INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Foreach
    [INFO] Created: 20 parsers.
    [INFO] Velocimacro : initialization starting.
    [INFO] Velocimacro : adding VMs from VM library template : VM_global_library.vm
    [ERROR] ResourceManager : unable to find resource 'VM_global_library.vm' in any resource loader.
    [INFO] Velocimacro : error using VM library template VM_global_library.vm : org.apache.velocity.exception.ResourceNotFoundException: Unable to find resource 'VM_glo
    bal_library.vm'
    [INFO] Velocimacro : VM library template macro registration complete.
    [INFO] Velocimacro : allowInline = true : VMs can be defined inline in templates
    [INFO] Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions
    [INFO] Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed.
    [INFO] Velocimacro : initialization complete.
    [INFO] Velocity successfully started.
    [INFO] [archetype:create]
    [INFO] Defaulting package to group ID: it.novaware
    Downloading: http://repo1.maven.org/maven2/org/richfaces/cdk/maven-archetype-jsf-component/3.1.1-GA/maven-archetype-jsf-component-3.1.1-GA.jar
    [INFO] ------------------------------------------------------------------------
    [ERROR] BUILD ERROR
    [INFO] ------------------------------------------------------------------------
    [INFO] Failed to resolve artifact.

    GroupId: org.richfaces.cdk
    ArtifactId: maven-archetype-jsf-component
    Version: 3.1.1-GA

    Reason: Unable to download the artifact from any repository

    Try downloading the file manually from the project website.

    Then, install it using the command:
    mvn install:install-file -DgroupId=org.richfaces.cdk -DartifactId=maven-archetype-jsf-component \
    -Dversion=3.1.1-GA -Dpackaging=jar -Dfile=/path/to/file


    org.richfaces.cdk:maven-archetype-jsf-component:jar:3.1.1-GA

    from the specified remote repositories:
    central (http://repo1.maven.org/maven2)


    [INFO] ------------------------------------------------------------------------
    [INFO] For more information, run Maven with the -e switch
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 6 seconds
    [INFO] Finished at: Sat Feb 23 23:17:50 EST 2008
    [INFO] Final Memory: 5M/9M
    [INFO] ------------------------------------------------------------------------

    By Anonymous Anonymous, At February 24, 2008 at 5:28 AM  

  • Very good article. Can I use CDK to extend existing RichFaces components? I want to extend tree component and add folder/document details. Details like Date Modified, Author and Size in the same row. Something like a MAC finder view.

    Is this easy to extend tree component with CDK? Or is CDK used only to create new components?

    By Anonymous Anonymous, At March 5, 2008 at 12:18 AM  

  • Excellant article...Thank you very much demetrio..

    Please refer the following link in caseanybody faces issues while trying out this article. RichFaces artifact version naming convention has been slightly changed between builds.

    http://www.jboss.com/index.html?module=bb&op=viewtopic&t=135836

    By Blogger Jobinesh Purushothaman, At May 31, 2008 at 6:44 AM  

  • Thanks to all...and sorry for being absent but I had a lot of work during those months...

    I updated the post (thanks jobinesh)...

    I did a lot of other works, have to find the time to write articles and maybe start a set of open source extra components!

    Is there someone want to help me? :)

    demetrio

    By Blogger demetrio.it, At July 6, 2008 at 3:54 PM  

  • Thanks demetrio, Very well written article... it got me started almost instantaneously.

    I am trying to create a custom component and want to collate components (both richfaces components and my other custom components). Is there a sample/reference that I can look at?

    By Anonymous Anonymous, At July 20, 2008 at 3:17 PM  

  • Terrible instructions, bitch.

    By Anonymous Anonymous, At November 6, 2008 at 9:57 PM  

  • I guess I'm the bitch. It was a problem with my maven path variable.

    By Anonymous Anonymous, At November 6, 2008 at 10:06 PM  

  • Negative and evil thoughts make the mind weak. The weak and uncontrolled mind always succumbs to calling those who are trying to help names.

    I thought it was a great tutorial. Thanks Demetrio.

    By Blogger iroh, At November 6, 2008 at 11:15 PM  

  • [url=http://www.onlinecasinos.gd]casino[/url], also known as accepted casinos or Internet casinos, are online versions of routine ("chunk and mortar") casinos. Online casinos franchise gamblers to filch up and wager on casino games reason the Internet.
    Online casinos typically offer odds and payback percentages that are comparable to land-based casinos. Some online casinos law higher payback percentages as a palliate into duty shindy games, and some talk known payout sherd audits on their websites. Assuming that the online casino is using an ostentatiously programmed indefinitely hundred generator, matter games like blackjack pocket an established congress edge. The payout tip-off tail of these games are established lifestyle the rules of the game.
    Varying online casinos charter out of the closet or affect into the property of their software from companies like Microgaming, Realtime Gaming, Playtech, Wide-ranging Prank Technology and CryptoLogic Inc.

    By Anonymous Anonymous, At January 15, 2013 at 12:55 AM  

  • Hello. Facebook takes a [url=http://www.freecasinobonus.gd]free casino bonus[/url] risk on 888 casino furnish: Facebook is expanding its efforts to alliance real-money gaming to millions of British users after announcing a apportion with the online gambling toss 888 Holdings.And Bye.

    By Anonymous Anonymous, At January 19, 2013 at 7:35 AM  

  • top [url=http://www.001casino.com/]001[/url] brake the latest [url=http://www.casinolasvegass.com/]casino las vegas[/url] manumitted no set aside bonus at the best [url=http://www.baywatchcasino.com/]baywatchcasino.com
    [/url].

    By Anonymous Anonymous, At January 19, 2013 at 4:31 PM  

  • [url=http://saclongchampa.deviantart.com/journal/]longchamp soldes[/url] In May 2009, I bought a bike at a garage sale in my neighborhood and started riding it. My favorite Loop is known locally as "Walnut Hill" -- it's about 14 miles long if I ride from my driveway. The first trip around took me two hours and four rest Purchase Vogue Mulberry Men Classic Tote Bag Black,2013 Mulberry Factory Shop York hot sale online. stops.
    [url=http://longchampbagsd.blog.cz/]sacs longchamps[/url] The Day The Earth Stood Still on Blu-ray has two perfect special features that capitalizes on unique aspects from the film. The first is the Theremin Here you are able to create your own piece of music and apply it to the iconic film using this strange device. The scene that they give you is Gort first appearance out of Highly Appreciated Mulberry Women's Small Bayswater Satchel Light Coffee Bag, one of the most luxurious designer brands, Mulberry has been a prominent force for nearly a century. Renowned for its leather goods and covetable ready to wear. the ship.
    [url=http://ameblo.jp/longchamppli/]longchamp soldes[/url] In 1980, could ii Germany 4 website of missile. This is the photo blogs are today. The camera disappeared. The Fossil Straw Bermuda Bucket is another top pick of straw handbags. The uniqueness of this handbag is displayed in its design and color. This handbag features a colorful striped fabric.. An elegant masterpiece, the exquisite accessory that makes you complete for a formal occasion. It is Mulberry Factory Shop outlet sale Mulberry Mini Alexa Leather Satchel Camel Bag sale for you the leather purse that is referred to in this valuable resource. Its perfect finish, shine, beautiful look, vibrant colors and looped embroidery make purses of such a material, extra ordinary.. In page, there are so quite a few web page to choose. Exact search is rather critical. It can guide you to come across site in superior-velocity.2. I usually use a square piece of fabric about the size of a tablecloth, lay the pillow in the center and then tie opposite corners in the middle on top of the pillow. Use the tied part as a carrying handle. It keeps the bobbins flat in transit..

    By Anonymous Anonymous, At March 3, 2013 at 1:43 PM  

  • It's really a great and helpful piece of information. I am happy that you shared this helpful information with us. Please keep us up to date like this. Thanks for sharing.

    Here is my web site PBA Dental

    By Anonymous Anonymous, At March 27, 2013 at 11:31 PM  

  • Hello there! Do you know if they make any plugins to
    protect against hackers? I'm kinda paranoid about losing everything I've worked hard on.
    Any tips?

    Here is my web blog - fymczy.com

    By Anonymous Anonymous, At April 19, 2013 at 12:13 AM  

  • Hi there mates, nice article and nice arguments commented at this place, I am
    in fact enjoying by these.

    Here is my page http://malkavians.500mb.net/index.php?action=Profile&U=6636

    By Anonymous Anonymous, At April 21, 2013 at 5:54 AM  

  • Becca Graham's parents Callum and Heather decided to freeze and store their daughter's milk teeth so the youngster can take advantage of future medical advances in stem cell research.


    Also visit my web blog ... ADP Dental Plans

    By Anonymous Anonymous, At April 21, 2013 at 10:24 AM  

  • Have you ever thought about creating an e-book or guest authoring on other websites?
    I have a blog based upon on the same ideas you discuss and would really
    like to have you share some stories/information.
    I know my viewers would enjoy your work. If you are even remotely
    interested, feel free to shoot me an email.


    Feel free to surf to my weblog; safe diets

    By Anonymous Anonymous, At April 23, 2013 at 7:33 PM  

  • By Anonymous Anonymous, At November 27, 2014 at 6:35 PM  

Post a Comment

Subscribe to Post Comments [Atom]



<< Home