• Home
  • RSS Feed
  • Log in

Tutorial: Master-Detail screen in Flex, backed up by Grails application
Posted by Maarten Winkels in the early afternoon: February 20th, 2008

In this blog I will show how to create a simple Master-Detail screen in Flex, how to back it up by an application is Grails and how to publish changes via JMS to all Flash clients. Important topics will be binding and remote java-object invocation in Flex and configuring JMS.

A Master-Detail view is a view with a master list, showing a collection of items and a detailed view, most often consisting of a form, in which the a single item can be edited. Clicking an item in the master list will display the details in the detail view.
Master-Detail

The combination of a framework for building RIA's (Flex) and a Java based dynamic framework for building services (Grails) seems very promising. I think the high productivity that can easily be achieved by this combination will make it a very attractive choice for your next big project.


In this blog we will implement a "double" master detail, as can be seen in the image above. A person may have multiple addresses. The list is an address book and the view displays a number of attributes of the people in the address book (first name, last name and city and country of their first address). If an item is selected, it is displayed in the adjacent form and the list in that form shows all addresses of that person. If an address is clicked its details are shown in the form below.

Binding

In flex we will use an ArrayCollection to contain the list and this list will be displayed using a DataGrid.

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" ...>
  <mx:ArrayCollection id="addressBook"/>
  <mx:DataGrid id="persons" editable="false" enabled="true" dataProvider="{addressBook}">
    <mx:columns>
      <mx:DataGridColumn headerText="First Name" dataField="firstName"/>
      <mx:DataGridColumn headerText="Last Name" dataField="lastName"/>
      <mx:DataGridColumn headerText="Primary City" dataField="firstAddressCity"/>
      <mx:DataGridColumn headerText="Primary Country" dataField="firstAddressCountry"/>
    </mx:columns>
  </mx:DataGrid >
...
</mx:Application>

The mechanisme that is used to get the data from the addressBook into the DataGrid is called "binding". This is done by using curly brackets around the expression, dataProvider="{addressBook}". Binding will make sure that if a change is made in the referenced object (in this case the addressBook or its content) the changes are reflected in the referrer (in this case the DataGrid). Binding works only in one direction! When the value in the referrer (DataGrid) is changed, the bound value (the addressBook) will not be updated. Some components (like the DataGrid) add this functionality and will update the underlying model. For this tutorial we will use the datagrid in editable="false" mode, since we want to edit the data by using the form.

The columns are filled by looking up the properties specified as dataField on the objects in the addressBook. But what are these objects? The are actionscript objects of the Person type.

package
{
  import mx.collections.ArrayCollection;
 
  [Bindable]
  public class Person
  {
    public var firstName:String;
 
    public var lastName:String;
 
    public var addresses: ArrayCollection = new ArrayCollection();
 
    public function get firstAddressCity():String {
      if (addresses.length == 0) {
        return "";
      }
      return addresses[0].city;
    }
 
    public function get firstAddressCountry():String {
      if (addresses.length == 0) {
        return "";
      }
      return addresses[0].country;
    }
  }
}

This class defines two simple properties and a collection. In addition it defines two getters that are also bound to in the DataGrid view. It is important to mark this class as [Bindable], so all properties of the class can be used in bind expressions.

Now when a row is selected in the list, we need to bind the values in the selected object to fields in a form. Then if the data in the form changes, we want to update the values in the selected item. Therefore we need Double Binding.

...
  <local:Person id="selPerson" firstName="{firstNameInput.text}" lastName="{lastNameInput.text}"/>
  <mx:Form>
    <mx:FormHeading label="Person Details"/>
    <mx:FormItem label="firstName">
      <mx:TextInput id="firstNameInput" text="{selPerson.firstName}"/>
    </mx:FormItem>
    <mx:FormItem label="lastName">
      <mx:TextInput id="lastNameInput" text="{selPerson.lastName}"/>
    </mx:FormItem>
  </mx:Form>
...

Now all we need to do is add some buttons and eventhandlers to the components...

...
  <mx:DataGrid id="persons" ... itemClick="doSelect(Person(event.currentTarget.selectedItem))">
...
  <mx:Button label="New Person" enabled="true" click="selPerson = new Person()"/>
  <mx:Button label="Remove Person" enabled="{persons.selectedItem != null}" click="doRemove(Person(persons.selectedItem))"/>
  <mx:Button label="Update Person" click="doUpdate(selPerson)"/>
  <mx:Script>
    <![CDATA[
      private function doSelect(p:Person): void {
        selPerson = p;
      }
      private function doRemove(p:Person): void {
        addressBook.removeItemAt(addressBook.getItemIndex(p));
      }
      private function doUpdate(p:Person):void {
        if (!addressBook.contains(p)) {
          addressBook.addItem(p);
        }
        selPerson = new Person();
      }
    ]]>
  </mx:Script>
...

... and the master detail view is done! The double binding will keep the fields in the form, the fields in the datagrid and the data in the model in synch.
The master-detail for the address works in much the same way and is a little different. A List is used to display the addresses in stead of a DataGrid and in the list the Address.toString() method is invoked to show the addresses. The eventhandling is also slightly different: A new Address is added to the list right away, instead of having an "Update Address" button.

Remoting

Now editing this data is surely fun, but they are not persistently stored nor shared between users. To do that, we'll nee to add some server component and remoting. Remoting from Flex can be done using BlazeDS from Adobe Labs. This is a "Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Adobe® Flex™". So we can write a Web application in Java to communicate to the backend using BlazeDS. In stead of starting to write a web.xml and a Servlet, let's look at Grails for a moment. This is a framework for creating web applications based on Java and Groovy and it has a flex plugin! That looks very promising.

First we create a new Grails application (grails create-app addresses) and download the flex-plugin (grails install-plugin flex). This takes some time to download and thus gives us some time to think. What do we really want to do in this Web application? We only need to domain classes (Person and Address) that should be persisted (this is standard with grails) and then have a service that exposes some methods to the flex front end. Saying this out loud takes almost as much time as writing it in Grails:

class Person {
 
  String firstName
  String lastName
 
  List addresses
 
  static hasMany = [addresses:Address]
  static fetchMode = [addresses:"eager"]
}

The Person class has the same two simple properties and the list of addresses. The list is declared as a hasMany relationship and the fetchmode is set to eager. We'll need to load the list of addresses for each person that we fetch, since otherwise the client will get LazyInitializationExceptions.

class Address {
  String street;
 
  int Number;
 
  String postalCode;	
 
  String city;
 
  String country;
}

The Address class is far simpler. It only contains the simple properties that the actionscript class contains.

class AddressBookService {
 
  static expose = ['flex-remoting']; 
 
  def List<Person> findAllPersons() {
    return Person.createCriteria().listDistinct {};
  }
 
  def Person get(id) {
    return Person.get(id);
  }
 
  def void update(Person p) {
    p.save();
  }
 
  def void remove(Person p) {
    p.delete(flush:true);
  }
}

The AddressBookService exposes itself as a flex-remoting service. This will make sure the Grails-Flex plugin will register the object with BlazeDS and it is made available to the Flex client.

The changes to the Flex code are pretty complex:

  1. We need to annotate the actionscript classes with the RemoteClass annotation, to indicate which ActionScript class must be used for which Java object.
    [RemoteClass(alias="Person")]

    Here the alias is the Java class name, in this case the Person class in the default package.

  2. We need to change our ActionScript classes to contain the generated domain class properties id and version. These properties are very important for persisting the data, since Hibernate (used by Grails) will find out by looking at the version whether this is a new or existing persistent object and by looking at the id what the identity of the existing object is. If we omit these fields from the action script classes, they will silently get lost during remoting and every object will look like a new object to Hibernate. It will thus do an insert for each new object.
        public var id:*;
     
        public var version:*;

    We don't care about the type of these properties from the ActionScript code, so we'll just use the wild cards.

  3. A RemoteObject is introduced, along with some event handling code.
      <mx:RemoteObject id="service" destination="addressBookService">
        <mx:method name="findAllPersons" result="setAddressBook(event.result)"/>
        <mx:method name="get" result="selPerson = Person(event.result)"/>
        <mx:method name="update"/>
        <mx:method name="remove"/>
      </mx:RemoteObject>
      <mx:Script>
        <![CDATA[
          private function setAddressBook(list:*):void {
            addressBook.removeAll();
            for each (var p:Person in list) {
              addressBook.addItem(p);
            }
          }
        ]]>
      </mx:Script>

    The destination of the RemoteObject maps directly on the class name of our grails service. The method declarations in the remote object map to its methods and the result event handler will be called asynchronously with the result of the method call when it is called from the Flex code.

  4. The eventhandling methods for our buttons have to be changed to use the new service. A "Refresh" button is also added to get the complete addressbook from the server.
      <mx:Button label="Refresh" click="service.findAllPersons()"/>
      <mx:Script>
        <![CDATA[
          private function doSelect(p:Person): void {
            service.get(p.id);
          }
          private function doRemove(p:Person): void {
            service.remove(p);
          }
          private function doUpdate(p:Person):void {
            service.update(p);
            selPerson = new Person();
          }
        ]]>
      </mx:Script>

    Notice that the handling of the result of a call to the service is not defined with the calling code: it is defined in the method declaration of the RemoteObject.
    Since we already structured the code with these changes in mind, the structural changes are fairly minimal. We only need to change the implementation of these three methods to make it work with the Grails service.

To get the application running, the .mxml and .as files need to be copied to the web-app directory of the Grails application. Here the Flex Webtier Compiler can pick up the source code and copile the application on request. if you go to http://localhost:8080/addresses/addresses.mxml, the interface will show up and it will be connected to the Grails application through BlazeDS.

That's really all there is to it! These 6 source files (addresses.mxml, Person.as, Address.as, AddressBookService.groovy, Person.groovy and Address.groovy) form a complete Rich GUI to an online AddressBook. None of the code is really complex and hardly any plumbing is needed.

While this is really great, I'm a little annoyed with this "Refresh" button and the fact that the user will only see changes made by others when he clicks this button. Lets see what we can do...

Server-push through JMS

Flex can be configured to listen to a JMS destination and Grails has a JMS plugin. We can use this to push a new data set to all clients whenever data is changed on the back-end. If we are going to use JMS, we need a JMS-provider, preferably one that can be embedded in a Grails application. Browsing the internet I found ActiveMQ from Apache, that is open source and can be embedded easily in a Spring application. After installing the JMS Plugin, downloading ActiveMQ and adding the necessary libraries to lib folder of the Grails application, we're all set.

  1. We need to configure a connectionFactory bean in spring.
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
      <bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
    <property name="connectionFactory">
          <bean class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL">
              <value>vm://localhost</value>
            </property>
          </bean>
        </property>
      </bean>
    </beans>

    This will create the connection factory and also start an embedded broker.

  2. We need to send the updates out to a topic whenever a change is made. This is done in the AddressBookService.
      def void update(Person p) {
        p.save();
        sendUpdate();
      }
      def void remove(Person p) {
        p.delete(flush:true);
        sendUpdate();
      }
      def private void sendUpdate() {
        try {
          sendPubSubJMSMessage("addresses", findAllPersons());
        } catch (Exception e) {
          log.error("Sending updates failed.", e);
        }
      }

    The sendPubSubJMSMessage method (added to all service classes by the Grails JMS plugin) is used to send a message to the "addresses" topic. The message will contain the new list of addresses.

  3. BlazeDS can be configured to lookup a JMS destination in JNDI. The services-config.xml needs to be changed to contain the JmsAdapter and the new destination for Flex clients to subscribe to.
        <service id="message-service" class="flex.messaging.services.MessageService" messageTypes="flex.messaging.messages.AsyncMessage">
     
          <adapters>
            <adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter" default="true" />
          </adapters>
     
          <destination id="updatesJmsTopic">
    <properties>
              <jms>
                <message-type>javax.jms.ObjectMessage</message-type>
                <connection-factory>ConnectionFactory</connection-factory>
                <destination-jndi-name>addresses</destination-jndi-name>
                <delivery-mode>NON_PERSISTENT</delivery-mode>
                <message-priority>DEFAULT_PRIORITY</message-priority>
                <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
    <transacted-sessions>false</transacted-sessions>
                <initial-context-environment>
    <property>
                    <name>Context.PROVIDER_URL</name>
                    <value>vm://localhost</value>
                  </property>
    <property>
                    <name>Context.INITIAL_CONTEXT_FACTORY</name>
                    <value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
                  </property>
    <property>
                    <name>topic.addresses</name>
                    <value>addresses</value>
                  </property>
                </initial-context-environment>
              </jms>
            </properties>
          </destination>
        </service>

    Configuring the JNDI context for Flex is actually quite tricky. The org.apache.activemq.jndi.ActiveMQInitialContextFactory is a very basic JNDI context and it uses properties to register queues and topics. Normally the properties would reside in a jndi.properties file. Here, these properties are specified in the service-config.xml itself. The property topic.addressesaddresses to register the topic with the physical name "addresses" (the property-value) in JNDI as "addresses" (the property-key-suffix). This is really an ActiveMQ JNDI oddity.

  4. Now the Flex client can be changed to listen to the topic
    <mx:Application ... creationComplete="service.findAllPersons(); consumer.subscribe()">
    ...
      <mx:Consumer id="consumer" destination="updatesJmsTopic" message="setAddressBook(event.message.body)"/>
    ...

    The Consumer is configured to listen to the "updatesJmsTopic", that is configured in the services-config.xml. Whenever a message comes in, its body is processed by the setAddressBook method, that will copy all the data to the ArrayCollection to be displayed in the DataGrid.
    Don't forget to subscribe the consumer at start up. We'll also load the initial data at start up.

This completes our RIA application. Updates will now be send through JMS to all Flex clients.

Conclusion

Creating a RIA with Flex and Grails is relatively simple. Developers can really focus on developing the GUI or the server side business logic. Most plumbing can be left to the sensible defaults and if not, changes can be made using "standard" frameworks like Spring or Hibernate. Whenever an advanced feature is necessary, developers can lean on their knowledge on Java or frameworks like Sping and Hibernate to quickly intergrate the functionality into the Grails application. I think these features make the Flex/Grails combo a very suitable candidate for developing RIAs on projects of all sizes.

A few remarks:

  1. It seems like duplication to have both the Grails domain classes and ActionScript classes. The Grails Flex plugin website hints at a feature that will generate the ActionScript classes on the fly. On the other hand, it might be usefull to separate the two, since in some occasions we need client-side business logic (like the firstAddressCity and firstAddressCountry properties) on the client-side domain objects. Nevertheless, this duplication is a bit brittle and errors (like typos in field names) are easily made.
  2. Currently the application is using an AMF channel and polling to send the updates to the clients. In Flex Data Services 2, a different type of channel (RTMPChannel) could be used to push data from the server to the client side. Unfortunately BlazeDS does not contain this functionality.

The sources of this application can be downloaded:

  • The sources for the local flex client: local-flex.zip
  • The sources for the grails application: grails-app.zip
  • The sources for the remote flex client: remote-flex.zip

The package with the complete grails application with the plugins was almost 60 MB, so to test the application, you'll have to create a Grails application and add the plugins manually.

  • Share/Bookmark

Filed under Flex, Grails | 21 Comments »



21 Responses to “Tutorial: Master-Detail screen in Flex, backed up by Grails application”



    Marcel Overdijk Says:
    Posted at: February 21, 2008 at 10:16 am

    Very good article! As the creator of the Grails plugin it’s nice to see it’s usage!

    Note that in the future I hope to extend the plugin with some scaffolding generation as well.

    Cheers,
    Marcel



    Will Rogers Says:
    Posted at: April 11, 2008 at 8:43 am

    This looks very handy. Thanks for your work. Incidentally, BlazeDS includes ActiveMQ, so it seems like you should not need a separate install of ActiveMQ.



    dersteppenwolf Says:
    Posted at: April 16, 2008 at 1:35 am

    great tutorial!!!



    Ryan Bard Says:
    Posted at: June 10, 2008 at 4:53 pm

    Great tutorial. Always nice to see practical examples. I use Flex with Python/Zope for the most part, so the Grails exposure was a plus as well.

    I noticed that the BlazeDS docs on Adobe Open Source indicate that “Real-time server push over standard HTTP” based on AMF is a part of the open source product. Is this a new development, maybe when Flex 3 was launched? Maybe this is the includion of ActiveMQ mentioned by Will Rogers above? Just curious as how this might change the tutorial.

    Thanks again!



    Rollo Tomazzi Says:
    Posted at: July 16, 2008 at 5:12 pm

    Kinda works once you go through the painful process of debugging without debuggers…
    Several things would be worth mentioning like, for example, the declaration of the remote object (
    ) in the mxml file which must come *before* using it, meaning at the top of the file. If you don’t, you’ll spend hours trying to figure out what’s wrong because of course, no error will pop up.
    Thx,
    Rollo



    Ron Lane Says:
    Posted at: July 17, 2008 at 4:36 pm

    Maarten,
    It’s been 5 months since you posted this very helpful piece. Have you gone further since then in our thoughts about integrating Grails and Flex. Have you found a way to eliminate the JMS to have the Flex changes be reflected back into the Grails underlying data?
    Ron



    Jonathan Morales Says:
    Posted at: October 18, 2008 at 6:42 pm

    HI,
    this is a really helpful article as we all know, but what about using this technologies in a production project.
    I have no problems whit grails nor flex nor even blazeDS but i have my doubts about the flex plugin. As you can see in the plugin page “It’s not sure if the plugin works in production mode as it is only tested in development mode”.
    what advise can i get from you.



    Jonathan Morales Says:
    Posted at: October 18, 2008 at 6:44 pm

    did i say thank you a lot?



    Len Says:
    Posted at: November 20, 2008 at 7:20 pm

    Thank you for tutorial. It certainly helps to understand how Flex remoting works.

    I downloaded sources and noticed that it is slightly different from the one in the text.

    Groovy to Flex binding is not quite clear. How does it marshal groovy domain objects to flex [bindable] ones. Is it all handled by groovy plug-in?



    Anatoliy Says:
    Posted at: January 14, 2009 at 11:36 am

    It’s a really a good tutorial!
    There is a new plug-in available ActiveMQ (it’s in develop mode now, I suppose it will not die)

    It’ll be great to update this tutorial.
    Before installing jms run command:
    grails install-plugin activemq

    There are some errors in addresses.mxml
    Tags
    Contain space char.



    Bens Blog » Flex/Java tutorials Says:
    Posted at: February 24, 2009 at 12:44 pm

    [...] Flex with Grails/BlazeDS [...]



    Luis Says:
    Posted at: May 31, 2009 at 1:28 am

    Has anyone tried this with Livecycle Data Services (DS) instead of BlaseDS? Should be about the same but with support for RTMPChannel.



    Luis Says:
    Posted at: May 31, 2009 at 1:37 am

    The Sources “Downloads” are not working. Thanks.



    Luis Says:
    Posted at: May 31, 2009 at 5:42 am

    What would be the steps in making an AIR App?



    David Says:
    Posted at: July 8, 2009 at 4:33 pm

    I am not even able to get working the medium variant with refresh button. I am using grails 1.1.1 with hibernate 3.3.3.1 and I always get:
    ERROR hibernate.LazyInitializationException – failed to lazily initialize a collection of role: Person.addresses, no session or session was closed



    Rag Says:
    Posted at: September 16, 2009 at 2:36 am

    Wonderful information and implementation.
    Unfortunately I can’t test the example because none of the zip file is downloaded.
    It seems that the files are deleted from the server.
    It will be nice if you can upload it again.



    kwame Says:
    Posted at: October 18, 2009 at 2:05 pm

    sorry download files don’t exist.
    Please upload them again.
    Thank you.
    Kwame



    Fedor Says:
    Posted at: November 4, 2009 at 2:52 pm

    To David:
    if you have LazyInitializationException try wrap your service method with DomainClass.withTransaction {}



    Fred Says:
    Posted at: January 19, 2010 at 11:55 pm

    A couple of other folks also asked for this so I’ll add my voice to the request, please make the three zip files available to us (or just let us know that you no longer plan to do that so that we stop asking). Thanks for doing that and thanks for the good posting.



    Fred Says:
    Posted at: January 20, 2010 at 12:08 am

    Here is the correct base URL for downloading the three files mentioned above. Just add each of the three zip file to this URL and you’ll be able to download this example.

    http://blog.xebia.com/wp-content/uploads/2008/02/



    Maarten Winkels Says:
    Posted at: January 23, 2010 at 4:44 am

    Hi Fred et. all.,

    I’ve updated the links for the download to work from whichever URL your using, so that should be fine now.

    Cheers,

    -Maarten



Leave a Reply

Click here to cancel reply.

Deployment automation for Java application running on Websphere, WebLogic and JBoss

Archives

  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009

Xebia Sites

  • Xebia Corporate
  • Xebia France
  • Xebia India

Categories

  • Java (279)
  • Agile (109)
  • General (50)
  • Testing (42)
  • Performance (42)
  • Hibernate (36)
  • Scrum (33)
  • Podcast (31)
  • Architecture (31)
  • Spring (28)
  • SOA (24)
  • Maven (22)
  • Project Management (22)
  • Flex (17)
  • JPA (17)
    • JPA implementation patterns (13)
  • Eclipse (15)
  • Quality Assurance (14)
  • Middleware (19)
  • Frameworks (13)

Tag Cloud

    Hibernate Closures Java Performance Semantic Web Maven product owner Introduction to Agile Seam Lean XML Xebia Groovy Grails IntelliJ Agile Scala JavaOne Poppendieck SOA Architecture Spring qcon fitnesse Ajax Testing Functional Programming Scrum esb Agile Awareness Workshop
medicin depression buy phentermine without a perscription aricept generic hair loss help how do you prevent bone loss urinary tract infection symptoms viagra sex domination cialis viagra cures for throat infection buy sumycin acne care new medication for cancer treatment help for sleeping problems on-line pharmacies cure snoring medications to help clot blood what is aspirin buy zestoretic bronchitis vs pneumonia back pain muscle acne face medication muscle women pain behind knee fat blocker man health arthritis natural cure woman health women insomnia cheap phentermine online cats and irritable bowel syndrome buy cialis generic online nutritional diet for osteoporosis abnormal blood clots treatments for hair loss what is zyprexa dental whitening products impotence herbs drugs for diabetes allergy prevention buy canada levitra Mentax adhd in children hair loss in woman medicines for blood clot online imitrex viagra buy free dog products clindamycin drug how to stop hair loss chloramphenicol discount drug viagra what valium does permanent hair loss heart failure medicine avapro 150mg ordering viagra online food allergies order viagra online online viagra prescription carisoprodol mg improve your skin discount erectile dysfunction medication buy xanax online buy order viagra scabies teatments information allegra vitamine b1 diazepam breast cancer support free stop smoking cipro side effects ultram cheapest treatment attention deficit disorder discount vitamins supplements how to get viagra online synthroid buy cheapest cialis zyrtec online how to clear acne preventive osteoporosis immune stimulants what is hoodia On Line Viagra getting over the pain diflucan dosage health asthma online stores hair loss products blood clot drugs colon parasites hair loss products discount medicine pravastatin buy griseofulvin tablets order indomethacin dog health products how to take a beta-blocker diazapan is valium treating cold sores chronic pain drug what is osteoporosis stress drug tooth whitening lowering cholesterol naturally legality of buying cialis online order levitra treatment for insomnia cheapest cialis index depakote overdose alprazolam condom sales treatment of yeast infection xanax sales taking viagra after cialis how to control pain new birth control chest pain health prozac prescription blood clots viagra in mexico chlamydia pill cancer drugs cold flu drugs how do i order viagra online super viagra acyclovir medicine benadryl dosage erythromycin pregnancy buy contoured condom chronic muscle pain pet health dogs treatment attention deficit disorder dental teeth whitening asthma medicine free prescription drugs herpes drug diabetes treatment buy tooth whitening gel cheap fast valium generic levitra buy cheapest viagra online lopressor drug pharmacy drug prices ultram dosing treatments for bipolar disorder neurontin withdrawal parasite medication chlamydia tips for increasing breast size ways to enhance breast what is valium used for metformin tablet order birth control hair loss for men how does xanax work treatment hepatitis c rythmol cheap acai antioxidants nexium generic blood pressure pills levitra online no prescription Levitra Online medications on line motion sickness drugs bactrim online order roxithromycin nicotine where can i order viagra immune supplements buy erexin v bph prostate allopurinol xanax for depression drug new smoking stop cheap impotence drug generic cialis delivery new treatment for depression antibiotics for cat viagra china alternative medicine cholesterol viagra dose anxiety disorder treatment severe muscle pain treatment of cancer calcium carbonate penis enlargement without pill valium maximum dosage reasons for high blood pressure energy product breast enlargement info cheap effexor building your body wrinkle cream aricept dosage alpha blocker increasing female sex drive valium depression new pain meds no rx xanax drug trileptal mg imitrex avapro 150mg medicine drugs contraception female claritin pill medication for acne med orders buy viagra internet levitra effect treatment for blood clots order sominex buy creatine buy precose cheap viagra overnight lopressor drug body building info health drugs general health and medical what is diazepam eye infections in dogs online prescription pills diclofenac tablet new medication anxiety buy citalopram medication male enhancement enhancement fat blocker medicine for throat infection order cardizem about soma health remedies for dogs generic xanax cheap zyrtec for depression medicine viagra sex domination buy acne skin care product hypnosis help study cure vaginal yeast infection weight loss supplement program muscle pain in leg how to increase erection buy viagra what is cla augmentin doses gaining muscle mass health med online heart rate treatments lopressor drug dog ear canal phentermine without prescription viagra order online weight loss glipizide diabetes astelin generic fat blocker buy gel tooth whitening cheap wellbutrin online weight loss program buy antiox anti-biotics acne skin treatmen tramadole vpxl pill drugs affecting levitra immune system support augmentin hypothyroidism medication buy erexin v uy prescription medication without a prescription buy discount order osteo arthritis online buy pilocarpine cheapest place to buy phentermine parasite treatment impotence help body fat loss viagra herb alternative constipation supplements treatment dementia adhd and medications muscle spasm relief viagra online cheap relieve upper back pain stop hair loss discount viagra online menstrual cycle problems antifungal shampoo side effects ativan gabapentin medication where can i buy viagra diazepam buy soma online clonidine dosage viagra gel top hair loss fast antibiotics cure chlamydia skin fungal infections drug zofran give up smoking alternative medicine cholesterol sleeping help best online viagra scams prednisone 10mg viagra sex domination lotensin easy weight loss pain meds without prescription over the counter drugs new high blood pressure medic generic compazine cetirizine drug order phentermine best fat blockers woman enhancement supplement drug zofran buy precose new drug treatment for cancer how to increase fertility viagra in australia benadryl dosing buy alcoholism medications order l arginine buy diazepam generic for ativan ativan prescription drugs weight loss treatment for chest pain woman health where can i buy phentermine online skin fungal infection give up smoking viagra on line hoodia information how does osteoporosis occur buy viagra online buy alcoholism medications depakote overdose klonopin pill tetracycline capsules what is high blood pressure bladder control for dogs generic for lipitor glucophage online pharmacy gabapentin dosage treating yeast infections dog health info cymbalta anxiety cheap tramadol without prescription hydrea drugs used for cancer cure for high blood pressure alcohol and valium relief from constipation liver infection treatment cialis soft zantac medication help sleep problems all natural antibiotics order medication without prescription sleep problems free hypnotherapy gaining muscle mass cheap viagra order online natural help for pain how to buy viagra drug price celebrex information otc diuretic levitra 10 mg buy medicine online pets products relief foot pain cialis without prescription med care cheapest generic cialis rapid hair loss pain medications generic side effects meds without prescriptions cat anxiety buy simplicef natural cure arthritis effects of high blood pressure lowest price generic viagra how to get birth control new breast cancer drug buy topamax blood pressure meds when are beta blockers prescribed how to get pain meds order fosamax online viagra name order viagra viagra cialis cat's eye health how to relieve lower back pain treating ear infections diazapan is valium online pain doctors high blood pressure in elderly medication to stop smoking wellbutrin dosages diabetes blood sugar levels weight loss diet pill side effects of prescribed pain pills drug list high blood pressure buy cialis online in usa ultram cost how to help osteoporosis how to use clomid discount brand viagra wellbutrin cymbalta buy pills without a prescription buy pain medicine online tab tramadol depression symptoms treatment how levitra work hypertension medications beta blockers prevent premature ejaculation xanax interactions with other medicines purchase medicine on line does alli work xenical mexico prescriptions buy sumycin uy prescription medication without a prescription ambien cost methocarbamol effects cheap beta blockers cats bladder reduce cholesterol naturally metformin tablet scabies medicine breast enhancer pills body building over 50 order viagra cheap zestril medication how to buy prescription medications online pharma kamagra drugs depression ear infection symptoms big muscle controlling blood pressure pain meds and pregnancy buy diazepam without prescription skin allergies antibiotic zoloft buy weight loss nutrition program Buy Cialis breast increase meds without prescriptions blood clots medical edema treatment for flu best hangover remedy diabetes drugs