• Home
  • RSS Feed
  • Log in

@Composite – Macro Annotations for Java
Posted by Andrew Phillips in the early morning: June 23rd, 2009

Some months ago I attended a presentation at which Wilfred Springer demonstrated his very cool Preon binary codec library. Defining binary file formats in Preon requires quite a lot of fairly repetitive sets of annotations, and during a chat after the talk Wilfred mentioned (in fact, he blogged about it) how much more convenient it would be if one could just define "shortcuts":

@RequiredEnumProperty(column = "AGENT")

for

@NotNull
@Column(name = "AGENT")
@Enumerated(EnumType.STRING)
 

for instance - and use those instead. Sort-of "macro annotatations" for Java, if you like.
A thought that has presumably also occurred to many frequent users of Hibernate, JAXB or other annotation-heavy frameworks.

Well, it took me rather longer than the couple of days it would probably have taken a developer of Wilfred's skill, but finally @Composite is here!

Just to dispel any misconceptions up front: here be no bytecode weaving or other runtime magic, so @Composite does not affect the semantics of the "regular" AnnotatedElement methods1. Composite annotations are instead supported via an AnnotatedElements interface, which provides all the familiar annotation-related methods, and "unpacks" registered composite annotations to their "leaf" types.
So @Composite is not (yet) drop-in magic - you will need to explicitly call the AnnotatedElements interface from your code.

Hello composite World

The AtCompositeDemo class included in the project basically looks like this:

public class AtCompositeDemo {
    ...
    // define a composite annotation
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    @CompositeAnnotation
    public @interface TargetRetentionLeafCompositeAnnotation {
        boolean runtimeRetention() default false;
 
        @LeafAnnotation
        Target targetLeafAnnotation() default @Target({ ElementType.METHOD });
 
        @LeafAnnotation(factoryClass = RetentionLeafAnnotationFactory.class)
        Retention retentionLeafAnnotation() default @Retention(RetentionPolicy.RUNTIME);
    }
 
    // apply the composite annotation...
    @Resource
    @TargetRetentionLeafCompositeAnnotation(runtimeRetention = true)
    private static @interface AnnotatedAnnotation {}
 
    // ...to two targets
    @Resource
    @TargetRetentionLeafCompositeAnnotation(runtimeRetention = false)
    private static @interface OtherAnnotatedAnnotation {}
 
    public static void main(String[] args) {
	// get a configured instance of the AnnotatedElements interface
        AnnotatedElements annotatedElements = ...
 
        log.info("Retrieving annotations from AnnotatedAnnotation.class");
        log.info(Arrays.toString(annotatedElements.getAnnotations(AnnotatedAnnotation.class)));
 
        log.info("Retrieving annotations from OtherAnnotatedAnnotation.class");
        log.info(Arrays.toString(annotatedElements.getAnnotations(OtherAnnotatedAnnotation.class)));
    }
 
}
 

When run, it should produce output similar to the following

Retrieving annotations from AnnotatedAnnotation.class
[@javax.annotation.Resource(shareable=true, mappedName=, description=, name=,
type=class java.lang.Object, authenticationType=CONTAINER), @java.lang.annotation.Retention(
value=RUNTIME), @java.lang.annotation.Target(value=[METHOD])]
Retrieving annotations from OtherAnnotatedAnnotation.class
[@javax.annotation.Resource(shareable=true, mappedName=, description=, name=,
type=class java.lang.Object, authenticationType=CONTAINER), @java.lang.annotation.Retention(
value=CLASS), @java.lang.annotation.Target(value=[METHOD])]

which demonstrates the key features of @Composite: namely, that the composite annotation is correctly "expanded" into a @Target and a @Retention annotation, and the "regular" @Resource annotation is still picked up.

Defining composite annotations

OK, so let's dive into this in a bit more detail. A composite annotation is just a regular custom annotation which is itself annotated with @CompositeAnnotation. Like any other annotation, it may have members, which may be of primitive or annotation type, amongst others. For instance

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@CompositeAnnotation
public @interface DoubleOhAgentCompositeAnnotation {
    ...
}
 

The "leaf" annotations - the annotations that the composite "expands" to - are simply members that are annotated with @LeafAnnotation. Of course, this only makes sense if these members return an annotation type! Note that members of a composite annotation are not automatically leaves, even if they return an annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@CompositeAnnotation
public @interface DoubleOhAgentCompositeAnnotation {
    // not a leaf annotation - not even an annotation type!
    int numberOfKills();
 
    // also not a leaf annotation, thus rather pointless to include
    Department employer default @Department("MI6");
 
    @LeafAnnotation
    CodeNumber codeNumberLeafAnnotation;
 
    @LeafAnnotation
    Clearance clearanceLeafAnnotation default @Clearance(SECRET);
}
 

As indicated in the comments, whilst there is nothing wrong with declaring non-leaf annotation members in the composite, it doesn't make much sense, in general, because the only purpose of the composite is to be expanded.

The values that will be returned for the leaf annotations are the values of the members of the composite instance that is being expanded. So the following composite instance

@DoubleOhAgentCompositeAnnotation(numberOfKills = 36, codeNumberLeafAnnotation = @CodeNumber("006"))
Agent jackGiddings;
 

would expand to @CodeNumber("006"), @Clearance(SECRET), and

@DoubleOhAgentCompositeAnnotation(numberOfKills = 103, codeNumberLeafAnnotation = @CodeNumber("007"),
    clearanceLeafAnnotation = @Clearance(TOP_SECRET))
Agent jamesBond;
 

to @CodeNumber("007"), @Clearance(TOP_SECRET).

Specifying good defaults almost always makes sense (if nothing else, it saves on typing!), but as you can see they can always be overriden, if required.

Writing leaf annotation factories

So what about this mysterious numberOfKills member? It's not of an annotation type, so certainly can't be a leaf. Moreover, it doesn't even have a default value, which means it must be specified every time the composite is used! Annoying, or what?

Well, I'll be getting to that. For now, it's worth bearing in mind that, whilst defining leaf annotations using defaults is already convenient, it's still static - the defaults are fixed in the definition, and even overriding defaults can only be done using values known at compile-time.

But what if the value of a leaf annotation depended on some runtime property - oh, I don't know, the time of day, the phase of the moon? Or if there was some non-trivial business logic involved that we would rather not have to carry out "by hand", as we would have to in order to specify the value at compile time?

Not that these cases are likely to occur often, but when they do, it would sure be nice to be able to generate the appropriate leaf annotation value dynamically.

This is where LeafAnnotationFactory comes in2. It generates a leaf value based on the composite annotation instance the leaf is declared in - and whatever else you can lay your hands on at runtime...even the phase of the moon, if you like.

As an example, assume that we wish to calculate a "danger rating" for our 00 agents:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@CompositeAnnotation
public @interface DoubleOhAgentCompositeAnnotation {
    ...
    @LeafAnnotation
    Rating ratingLeafAnnotation;
}
 

The current rules indicate that any agent with more than 100 kills - ah, yes, the numberOfKills member - is considered extremely dangerous.

Of course, we know that Maj. Jack Giddings is below and Bond above the limit, so we could just set the values at compile time.

@DoubleOhAgentCompositeAnnotation(numberOfKills = 36, codeNumberLeafAnnotation = @CodeNumber("006"),
    ratingLeafAnnotation = @Rating(DANGEROUS))
Agent jackGiddings;
 
@DoubleOhAgentCompositeAnnotation(numberOfKills = 103, codeNumberLeafAnnotation = @CodeNumber("007"),
    clearanceLeafAnnotation = @Clearance(TOP_SECRET),
    ratingLeafAnnotation = @Rating(EXTREMELY_DANGEROUS))
Agent jamesBond;
 

But what if Her Majesty's government, in a fit of political correctness, lowers the limit to 30? Will we remember to adjust the annotations? Will we want to do it for all of our agents?

No, better to generate the correct value for ratingLeafAnnotation at runtime.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@CompositeAnnotation
public @interface DoubleOhAgentCompositeAnnotation {
    int numberOfKills();
    ...
    /*
     * The default will be *ignored* - the factory is always called - but it is more
     * convenient to provide it to prevent the compiler from prompting for one.
     */
    @LeafAnnotation(factoryClass = RatingLeafAnnotationFactory.class)
    Rating ratingLeafAnnotation default @Rating(DANGEROUS);
}
 
class RatingLeafAnnotationFactory
        implements LeafAnnotationFactory<Rating, DoubleOhAgentCompositeAnnotation> {
    private static final int EXTREME_DANGER_THRESHOLD = 100;
 
    public Rating newInstance(
            DoubleOhAgentCompositeAnnotation declaringCompositeAnnotation) {
        return RuntimeConfiguredAnnotationFactory.newInstance(Rating.class,
                MapUtils.<String, Object>toMap("value",
                        (declaringCompositeAnnotation.numberOfKills() > EXTREME_DANGER_THRESHOLD)
                        ? Rating.EXTREMELY_DANGEROUS
                        : Rating.DANGEROUS));
    }
 
}
 
// @Rating calculated based on the number of kills
@DoubleOhAgentCompositeAnnotation(numberOfKills = 36, codeNumberLeafAnnotation = @CodeNumber("006"))
Agent jackGiddings;
 
@DoubleOhAgentCompositeAnnotation(numberOfKills = 103, codeNumberLeafAnnotation = @CodeNumber("007"),
    clearanceLeafAnnotation = @Clearance(TOP_SECRET))
Agent jamesBond;
 

Now all we need to adapt to the new rules is to modify the EXTREME_DANGER_THRESHOLD in the factory3!

Note that the default defined (somewhat counter-intuitively, perhaps) for ratingLeafAnnotation has no effect on the value: the factory is always called. But without a default the compiler will prompt for a value for the member whenever the composite is used.

Usage guidelines

In order to forestall any chance of your mind taking off on imagining all the things one might do with this, a few sobering points:

  • Firstly, remember that @Composite currently only works if annotation reflection is done via the AnnotatedElements interface. Class.getAnnotation(...) will not work here!
  • This also means that, regrettably, @Composite will not work for Hibernate, Spring or any other framework that internally uses "regular" methods to access annotation information.4
  • For the same reasons, @Composite should not be used to provide annotations which are used by Java itself, e.g. @Target or @Retention, as these won't be visible to the compiler! So please don't go about copying the demo ;-)

Restrictions

Apart from the above guidelines, there are a number of intentional limitations in the use of composites, whose aim is to make sure that @Composite does not allow you to work around restrictions on the use of "regular" annotations. This ensures that annotation semantics remain consistent with regular Java.

Which means that, amongst others:

  • There may also only be one leaf annotation of a given type per composite.
  • The target of a leaf annotation must match that of the composite annotation in which it is declared.
  • There may not be more than one annotation of the same type on a given element, whatever the regular and composite annotations on that element.

For the full list, see LeafAnnotation and CompositeAnnotation.

Using the validation processor

In most of the above cases, @Composite will fail early, at the moment the AnnotatedElements instance is created. Still, it would be nicer if invalid configurations - and as we have seen, there are quite a few ways of creating those - could be detected before we even get to runtime.

To help accomplish this, @Composite includes a validation processor, which is a Java 6 annotation processor5. If you're running on Java 6, the processor will automatically run at compile-time6 and fail, with hopefully useful error messages, if @Composite is not being used correctly. You can also add validation processing to your Eclipse project - see the CompositeAnnotationValidationProcessor Javadoc for details.

For a demonstration of the validation processor, check out the at-composite-validator-demo project (http://aphillips.googlecode.com/svn/at-composite-validator-demo/trunk/) and try to build it!

Integrating with Spring

@Composite wouldn't be a self-respecting Java project if it didn't offer some kind of Spring integration now, would it7? Thankfully, there's not much to do here - simply create an instance of
AtCompositeAnnotatedElements (which can be a singleton), passing a list of the composite annotation types to be supported.

AtCompositeDemo already demonstrates how to use @Composite with Spring, and more information can be found in the AtCompositeAnnotatedElements Javadoc.

Getting @Composite

The @Composite source code, and that for non-standard dependencies, is available at Google Code.

Maven

If you're using Maven, the relevant dependency is

  <dependency>
    <groupId>com.qrmedia.pattern</groupId>
    <artifactId>at-composite</artifactId>
    <version>1.0-SNAPSHOT</version>
  </dependency>
 

and you'll need to add the following repository to your POM.

  <repository>
    <id>qrmedia-releases</id>
    <url>http://qrmedia.com/mvn-repository</url>
  </repository>
 
Footnotes

  1. Although that would probably make an interesting follow-up project!
  2. I studiously ignored its appearance in the demo, but I'm pretty sure I didn't manage to sneak it past all of you ;-)
  3. RuntimeConfiguredAnnotationFactory is just a convenient way of generating a runtime annotation instance, but well worth a look...if only to see that it's probably simpler than one might expect!
  4. I suspect, though, that it wouldn't be too hard to adapt such frameworks to use @Composite, since it is fully "backwards-compatible" with regular Java annotations. With judicious use of aspects, there might even be a way of "retro-fitting" this onto existing code.
  5. Testing the annotation processor proved rather less straightforward than I first imagined, but that's a story for another blog post.
  6. You can suppress annotation processing by passing a -proc:none argument to the compiler. See the javac docs.
  7. Although I have to confess to secret Guice leanings of late ;-)
  • Share/Bookmark

Tags: annotation, composite, Java, macro
Filed under Java, Spring | 6 Comments »



6 Responses to “@Composite – Macro Annotations for Java”



    Wilfred Springer Says:
    Posted at: June 23, 2009 at 9:59 am

    Totally cool. I’m just wondering if it would be possible to have a general purpose LeafAnnotationFactory that’s built on top of some sort of EL. (Which would pull the derivation rule back into an annotation attribute.)



    Andrew Phillips Says:
    Posted at: June 23, 2009 at 10:12 am

    NB: Trying to build this on a few different machines has thrown up some unexpected test failures in CompositeAnnotationValidationProcessorTest. This uses the Java Compiler API and doesn’t appear to be exactly bullet-proof yet – the tests in this class can be @Ignored, and I’ll try to figure out what’s going on asap.

    Also, running eclipse:eclipse or eclipse:m2eclipse against the project will leave you with a

    <classpathentry kind="src" path="src/test/java/com/qrmedia/pattern/compositeannotation/validation/sample" output="target/test-classes/com/qrmedia/pattern/compositeannotation/validation/sample" excluding="**/*.java"/>

    entry in your .classpath. This is a (seemingly unavoidable) result of the way the test resources are configured in the POM – again, required for the validation processor tests – and needs to be removed.
    Or right-click on the source folder in Eclipse and select Build Path -> Remove from Build Path.



    fuzebest Says:
    Posted at: June 26, 2009 at 9:34 am

    why did you abandon the idea of bytecode or runtime instrumentation?
    Nowadays it is nothing wrong with it.



    Andrew Phillips Says:
    Posted at: June 26, 2009 at 10:25 pm

    @fuzebest “abandoned” is perhaps an unfortunate choice of phrase – it was intentionally not considered for a number of reasons, chief among which is that it would limit factory classes to once again essentially producing static annotations. The “leaf” annotation(s) would namely have to be generated at the moment the bytecode were instrumented, as opposed to when the lookup is made.

    But it is definitely an avenue worth pursuing, and certainly a possibility with regard to trying to integrate composite annotations with existing code.



    The sceptic Says:
    Posted at: August 16, 2009 at 2:01 pm

    Hi Andrew,

    sorry, but I didn’t get the point. You just started to provide a shortcut for the tedious task of writing annotation triples (see your motivation example) and you end up with a composite-wrapper that contains literaly exactly that three annotations requiring to type even more characters for the wrapping-stuff? “Yes, you can” save typing effort in cases were you only want to use defaults for the members of a composite…but in any other case using the composition scheme is no better than using the original source annotations. You might argue that in cases I want to change all occurences of a certain scheme than I just have to change the composite. Yes, true, but then I would argue that it’s probably never going to happen if you work with a set of stable annotations. And even if there’s change then I would rather favour a text-processing tool. What you really wanted to do was to provide a c-style preprocessor, but disgiused it in some strange annotation scheme. I guess ant is capable of doing preprocessing stuff…
    As I am “The sceptic” I have been waiting ever since annotations arrived for the first projects doing funny stuff with it: Now here’s another example. When is “jakarta commons annotations” going to arrive, which is the (meta) annotation wrapper (like this wrapper for logging frameworks…). And I like the Meta-Annotations of webbeans or what was the name of the next big thing. What I wanted to say with this rant is that you should concider using the right tool for a task, in oyur case a (macro) preprocessor…

    My suggstions:
    1.) Sun/Java starts providing a preprocessor api (I don’t get the point why anybody refuses to concider a preprocessor, but any other funny stuff is discussed in epic breadth and blind enthusiasm, for instance making java functional…)

    2.) The Java framework itself should provide a plugable SPI-API for resolving annotations. Annotations should be allowed to have any number of arguments. A Resolver-SPI-Plugin (SPI function) could analyze the source annotations and provide an interpretation/tranformation into a target set. We would require

    2.1 Declaration
    @AnnotationBlaBla
    @AnnotationSPI(id=FunnyMapper)
    public @interface FunnyInterpretedAnnotation {
    String useCaseID;
    AnnotaionParamList nameValueVarargs;
    }

    2.2 How to use the Annotation
    @FunnyInterpretedAnnotation(UseCaseID=”Bond”)
    Agent ofYourMajesty;

    2.3 The SPI trafo function
    function: SPI( @FunnyInterpretedAnnotation(UseCaseID=”Bond”) ) -> { @NotNull, @CodeNumber(”007″), @Clearance(”Clarence”) }

    2.4 SPI function configuration
    And then we need some sort of configuration, mapping UseCaseIDs into sets of target annotations…well let’s use xml for that. Oh, wait a minute! annotations were invented partially to overcome xml-hell. Well, seems to be a silly suggeestion then…



    Andrew Phillips Says:
    Posted at: August 19, 2009 at 4:42 pm

    @The sceptic Replacing three annotations on an element with a “special” annotation that contains those three annotations would indeed be rather pointless. However, it’s not uncommon, to see a group of annotations repeated many times throughout a codebase. This aside from the potential benefits (and drawbacks, of course) of making the annotations runtime-configurable, for instance

    > Sun/Java starts providing a preprocessor api
    Something in addition to the instrumentation API, or replacing it?

    The SPI concept is interesting, and would certainly offer a much cleaner way of making the annotation system pluggable, i.e. integrating it with “standard” Java annotation reflection.



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

    esb Introduction to Agile Performance Maven Poppendieck Ajax Lean Hibernate Functional Programming Closures Seam XML Testing Xebia Agile fitnesse SOA Scrum Architecture Agile Awareness Workshop qcon Grails Java product owner Spring Groovy IntelliJ JavaOne Scala Semantic Web
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