Spring – Hibernate – JAX-RS Putting Hibernate to REST

This pattern exposes your domain model as a http REST interface. It violates MVC and many other good practices but it’s way cool and easy to not blog about it. There are certain scenarios too where you might want to expose your model this way, for example if you’re not building an app but a framework to build apps and you want to give raw access to some of the core model beans or data.

In most examples I found on the web, all REST services go through services before they hit the model and I wanted an automatic way to access the model by simply adding a few annotations to the beans.

This pattern allows you to

  1. List objects by class in a paginated fashion
  2. !!! List objects properties in a paginated fashion
  3. Update objects
  4. Get individual object properties
  5. !!! Update individual object properties
  6. automatically serialize / desiarilize in xml, json, etc…

I have not added search but the implementation is trivial based on Hibernate Search by simply annotating the entities and properties.
The code also does not go into the details in handling aggregated collections @OneToMany, @ManyToMany, @Any or agregated objects @ManyToOne

The code uses some non standard way of doing things such as injection of a Service in one of the entities base class properties.

This code in this article and some other examples are available for download with svn:
svn checkout http://raulrajatutorials.googlecode.com/svn/trunk/ raulrajatutorials-read-only
Comments in the code itself should be self-explanatory if you already have some java + spring experience.

See embedded video for a demo of the code at work

Demo video

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <description>hibernate-rest</description>
    <display-name>hibernate-rest</display-name>
 
    <!-- Spring config  -->
    <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>
               /WEB-INF/conf/applicationContext.xml
           </param-value>
       </context-param>
 
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/conf/log4j.properties</param-value>
    </context-param>
 
 
    <!-- Spring Listeners -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
 
    <filter>
        <filter-name>openSessionInViewFilter</filter-name>
        <filter-class>
            org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        </filter-class>
    </filter>
 
    <filter-mapping>
        <filter-name>openSessionInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
 
    <!-- Remote web services -->
       <servlet>
           <servlet-name>CXFServlet</servlet-name>
           <servlet-class>
               org.apache.cxf.transport.servlet.CXFServlet
           </servlet-class>
       </servlet>
 
       <servlet>
           <servlet-name>remoteApiBasicHttpHandlerImpl</servlet-name>
           <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
       </servlet>
 
 
       <!-- Remote web services mapping -->
       <servlet-mapping>
           <servlet-name>CXFServlet</servlet-name>
           <url-pattern>/api/*</url-pattern>
       </servlet-mapping>
 
 
</web-app>

hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory name="com.raulraja.tutorials">
        <!-- Persistent entities -->
        <mapping class="com.raulraja.model.domain.Person"/>  
    </session-factory>
</hibernate-configuration>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:cxf="http://cxf.apache.org/core"
        xmlns:jaxws="http://cxf.apache.org/jaxws"
        xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:util="http://www.springframework.org/schema/util"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
       http://cxf.apache.org/core
       http://cxf.apache.org/schemas/core.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd
       http://cxf.apache.org/jaxrs
       http://cxf.apache.org/schemas/jaxrs.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
        default-autowire="byName">
 
    <!-- Load CXF modules from cxf.jar -->
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml"/>
 
 
    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus>
 
    <!-- A template instance to expose as the service -->
    <bean id="person" class="com.raulraja.model.domain.Person"/>
 
    <!-- The rest server configuration -->
    <jaxrs:server id="restServer" address="/rest/">
        <jaxrs:serviceBeans>
            <ref bean="person"/>
        </jaxrs:serviceBeans>
        <jaxrs:extensionMappings>
            <entry key="feed" value="application/atom+xml"/>
            <entry key="json" value="application/json"/>
            <entry key="xml" value="application/xml"/>
            <entry key="html" value="text/html"/>
        </jaxrs:extensionMappings>
    </jaxrs:server>
 
    <!-- The datasource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="org.postgresql.Driver"/>
        <property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/tutorials?autoReconnect=true"/>
        <property name="user" value="tutorials"/>
        <property name="password" value="tutorials"/>
    </bean>
 
    <!-- The hibernate session factory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocations">
            <list>
                <value>WEB-INF/conf/hibernate.cfg.xml</value>
            </list>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>
 
    <!-- A persistence adapter implementation that acts as a data access service -->
    <bean id="persistenceAdapter" class="com.raulraja.model.persistence.impl.HibernatePersistenceAdapterImpl" autowire="autodetect">
        <property name="defaultResultSize" value="10"/>
        <property name="hibernateTemplate">
            <ref bean="hibernateTemplate"/>
        </property>
    </bean>
 
    <!-- The transaction manager -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
 
    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
 
    <!-- The hibernate template -->
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
 
    <!-- statically injects the persistence service in the entities base class -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="com.raulraja.model.domain.PersistentEntity.setPersistenceAdapter"/>
        <property name="arguments">
            <ref bean="persistenceAdapter"/>
        </property>
    </bean>
 
 
</beans>

PaginatedList.java

package com.raulraja.model.domain;
 
import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
 
/**
 * An object that encapsualtes pagination over list of records
 */
@XmlRootElement(namespace = PersistentEntity.NAMESPACE)
@XmlType(name = "paginatedList", namespace = PersistentEntity.NAMESPACE)
public class PaginatedList<Entity> {
 
	/**
	 * the total amount of items available in the data store
	 */
	private long count;
 
	/**
	 * the starting record displayed on the list
	 */
	private long start;
 
	/**
	 * the size of this list
	 */
	private long size;
 
	/**
	 * the records contained on the list
	 */
	private List<Entity> items = new ArrayList<Entity>();
 
	/**
	 * default constructor
	 */
	public PaginatedList() {
		super();
	}
 
	/**
	 * Constructor that takes the data
	 * @param count the total amount of items available in the data store
	 * @param start the starting record displayed on the list
	 * @param items the records contained on the list
	 */
	public PaginatedList(long count, long start, List<Entity> items) {
		this();
		setCount(count);
		setStart(start);
		setItems(items);
		setSize(items.size());
	}
 
	/**
	 * @return the total amount of items available in the data store
	 */
	public long getCount() {
		return count;
	}
 
	public void setCount(long count) {
		this.count = count;
	}
 
	/**
	 * @return the starting record displayed on the list
	 */
	public long getStart() {
		return start;
	}
 
	public void setStart(long start) {
		this.start = start;
	}
 
	/**
	 * @return the size of this list
	 */
	public long getSize() {
		return size;
	}
 
	public void setSize(long size) {
		this.size = size;
	}
 
	/**
	 * @return the records contained on the list
	 */
	@XmlElementWrapper(name = "items")
	@XmlElements({
            @XmlElement(name="person", type=Person.class, namespace = PersistentEntity.NAMESPACE),
			@XmlElement(name="property", type=Object.class, namespace = PersistentEntity.NAMESPACE)
    })
	public List<Entity> getItems() {
		return items;
	}
 
	public void setItems(List<Entity> items) {
		this.items = items;
	}
}

PersistentEntity.java

package com.raulraja.model.domain;
 
import com.raulraja.model.persistence.PersistenceAdapter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.transaction.annotation.Transactional;
 
import javax.ws.rs.*;
import java.lang.reflect.InvocationTargetException;
 
/**
 * Base class for entities with static access to the persistent context
 */
public abstract class PersistentEntity<Entity extends PersistentEntity> {
 
	public static final String NAMESPACE = "https://apps.raulraja.org/api";
 
	private static PersistenceAdapter<PersistentEntity> persistenceAdapter;
 
	public static void setPersistenceAdapter(PersistenceAdapter<PersistentEntity> persistenceAdapter) {
		PersistentEntity.persistenceAdapter = persistenceAdapter;
	}
 
	public abstract Long getId();
 
	public abstract Class<Entity> getMappedClass();
 
	@Override
	@SuppressWarnings("unchecked")
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
 
		PersistentEntity<?> entity = (PersistentEntity<?>) o;
 
		if (getId() != null ? !getId().equals(entity.getId()) : entity.getId() != null) {
			return false;
		}
 
		return true;
	}
 
	@Override
	public int hashCode() {
		return getId() != null ? getId().hashCode() : 0;
	}
 
 
	/**
	 * Returns a paginated list of all entities for this class
	 * @param start the start record
	 * @param size the limit
	 * @return the list of matches
	 */
	@GET @Path("list")
	@SuppressWarnings("unchecked")
    public PaginatedList<Entity> list(@QueryParam("start") int start, @QueryParam("size") int size) {
		Class mappedClass = getMappedClass();
		PaginatedList<Entity> list = (PaginatedList<Entity>) persistenceAdapter.list(mappedClass, start, size);
		return list;
	}
 
	/**
	 * Returns a paginated list of all entities for this class
	 * @param start the start record
	 * @param size the limit
	 * @return the list of matches
	 */
	@GET @Path("{property}/list")
	@SuppressWarnings("unchecked")
    public PaginatedList<Object> list(@PathParam("property") String property, @QueryParam("start") int start, @QueryParam("size") int size) {
		Class mappedClass = getMappedClass();
		PaginatedList<Object> list = (PaginatedList<Object>) persistenceAdapter.list(mappedClass, property, start, size);
		return list;
	}
 
 
	@GET @Path("{id}")
    public PersistentEntity get(@PathParam("id") Long id) {
		Class mappedClass = getMappedClass();
		PersistentEntity entity = persistenceAdapter.get(mappedClass, id);
		return entity;
	}
 
	@GET @Path("{id}/{property}")
    public Object getProperty(@PathParam("id") Long id, @PathParam("property") String property) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		Class mappedClass = getMappedClass();
		PersistentEntity entity = persistenceAdapter.get(mappedClass, id);
		return PropertyUtils.getProperty(entity, property);
	}
 
	@POST @Path("{id}/{property}")
	@Transactional
    public Object updateProperty(@PathParam("id") Long id, @PathParam("property") String property, String value) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		Class mappedClass = getMappedClass();
		PersistentEntity entity = persistenceAdapter.get(mappedClass, id);
		BeanUtils.setProperty(entity, property, value);
		return persistenceAdapter.save(entity);	
	}
 
	@POST @Path("create")
	@Transactional
    public PersistentEntity create(Entity entity) {
		PersistentEntity persistentEntity = get(entity.getId());
		if (persistentEntity != null) {
			throw new IllegalStateException("There is already a " + getMappedClass() + " with id = " + entity.getId());
		}
		persistenceAdapter.save(entity);
		return entity;
	}
 
	@POST @Path("update")
	@Transactional
    public PersistentEntity update(Entity entity) {
		PersistentEntity<Entity> persistentEntity = get(entity.getId());
		if (persistentEntity == null) {
			throw new IllegalStateException("There is no" + getMappedClass() + " with id = " + entity.getId());
		}
		persistentEntity.updateFromRequest(entity);
		persistenceAdapter.save(persistentEntity);
		return persistentEntity;
	}
 
	public abstract Entity updateFromRequest(Entity entity);
 
	@POST @Path("delete")
    public Entity delete(Entity entity) {
		persistenceAdapter.delete(entity);
		return entity;
	}
 
}

Person.java

package com.raulraja.model.domain;
 
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.ws.rs.Path;
import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
 
@Entity
@Path("/person/")
@XmlRootElement
@XmlType(name = "person", namespace = PersistentEntity.NAMESPACE)
public class Person extends PersistentEntity<Person> {
 
	@Id
	private Long id;
 
	private String name;
 
	private String email;
 
	@ManyToOne
	private Person parent;
 
	@OneToMany(mappedBy = "parent")
	private List<Person> children = new ArrayList<Person>();
 
	@XmlElement(type = Long.class)
	public Long getId() {
		return id;
	}
 
	public void setId(Long id) {
		this.id = id;
	}
 
	@XmlElement(type = String.class)
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	@XmlElement(type = String.class)
	public String getEmail() {
		return email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
 
	@XmlElement(type = Person.class)
	public Person getParent() {
		return parent;
	}
 
	public void setParent(Person parent) {
		this.parent = parent;
	}
 
	@XmlElement(type = Person.class, name = "person", namespace = PersistentEntity.NAMESPACE)
	@XmlElementWrapper(name = "children")
	public List<Person> getChildren() {
		return children;
	}
 
	public void setChildren(List<Person> children) {
		this.children = children;
	}
 
	public Class<Person> getMappedClass() {
		return Person.class;
	}
 
	public Person updateFromRequest(Person person) {
		setName(person.getName());
		setEmail(person.getEmail());
		return this;
	}
 
 
}

PersistenceAdapter.java

package com.raulraja.model.persistence;
 
import com.raulraja.model.domain.PaginatedList;
 
import java.io.Serializable;
 
 
public interface PersistenceAdapter<Entity> {
 
	/**
	 * Returns a paginated list of all entities for this class
	 * @param entityClass the entity class
	 * @param start the start record
	 * @param limit the limit
	 * @return the list of matches
	 */
	PaginatedList<Entity> list(Class<Entity> entityClass, int start, int limit);
 
	/**
	 * Returns a paginated list of the selected property for this class
	 * @param entityClass the entity class
	 * @param start the start record
	 * @param limit the limit
	 * @return the list of matches
	 */
	PaginatedList<?> list(Class<Entity> entityClass, String property, int start, int limit);
 
    /**
     * Find a persistent object based on its unique id
     * @param entityClass the entity's class
     * @param id unique identifier
     * @return returns a persistent object
     */
    public Entity get(Class<Entity> entityClass, Serializable id);
 
    /**
     * Persists an object
     * @param entity to be persisted
	 * @return the saved entity
     */
    public Entity save(Entity entity);
 
    /**
     * Removes an entity from the data store
     * @param entity to be removed
	 * @return the deleted entity
     */
    public Entity delete(Entity entity);
 
 
}

HibernatePersistenceAdapterImpl.java

package com.raulraja.model.persistence.impl;
 
import com.raulraja.model.domain.PaginatedList;
import com.raulraja.model.persistence.PersistenceAdapter;
import org.apache.log4j.Logger;
import org.hibernate.*;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
 
import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
 
/**
 * Hibernate spring based impl of the persistence adapter
 */
public class HibernatePersistenceAdapterImpl<Entity> extends HibernateDaoSupport implements PersistenceAdapter<Entity> {
 
	private final static Logger log = Logger.getLogger(HibernatePersistenceAdapterImpl.class);
 
	private Integer defaultResultSize = 10;
 
	public void setDefaultResultSize(Integer defaultResultSize) {
		this.defaultResultSize = defaultResultSize;
	}
 
	/**
	 * Returns a paginated list of all entities for this class
	 *
	 * @param entityClass the entity class
	 * @param start	   the start record
	 * @param limit	   the limit
	 * @return the list of matches
	 */
	@SuppressWarnings("unchecked")
	public PaginatedList<Entity> list(Class<Entity> entityClass, int start, int limit) {
		limit = limit > 0 ? limit : defaultResultSize;
		if (entityClass == null)
            return null;
 
        List<Entity> entities;
		PaginatedList<Entity> page = new PaginatedList<Entity>();
 
		int total;
		try {
			DetachedCriteria criteria = DetachedCriteria.forClass(entityClass);
			entities = (List<Entity>) getPreparedHibernateTemplate().findByCriteria(criteria, start, limit);
			total = count(entityClass);
			page = new PaginatedList<Entity>(total, start, entities);
        }
        catch (Throwable e) {
            log.error(e, e);
        }
        return page;
	}
 
	/**
	 * Returns a paginated list of the selected property for this class
	 *
	 * @param entityClass the entity class
	 * @param start	   the start record
	 * @param limit	   the limit
	 * @return the list of matches
	 */
	public PaginatedList<?> list(Class<Entity> entityClass, String property, int start, int limit) {
		limit = limit > 0 ? limit : defaultResultSize;
		if (entityClass == null)
            return null;
 
        List<Object> properties;
		PaginatedList<Object> page = new PaginatedList<Object>();
 
		int total;
		try {
			DetachedCriteria criteria = DetachedCriteria.forClass(entityClass);
			criteria.setProjection(Projections.property(property));
			properties = (List<Object>) getPreparedHibernateTemplate().findByCriteria(criteria, start, limit);
			total = count(entityClass);
			page = new PaginatedList<Object>(total, start, properties);
        }
        catch (Throwable e) {
            log.error(e, e);
        }
        return page;
	}
 
	private Integer count(final Class<Entity> entityClass) {
		return (Integer) getPreparedHibernateTemplate().execute(new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(entityClass);
				criteria.setProjection(Projections.rowCount());
				return criteria.list().get(0);
			}
		});
	}
 
	/**
	 * Find a persistent object based on its unique id
	 *
	 * @param entityClass the entity's class
	 * @param id		  unique identifier
	 * @return returns a persistent object
	 */
	@SuppressWarnings("unchecked")
	public Entity get(Class<Entity> entityClass, Serializable id) {
		Entity entity = (Entity) getHibernateTemplate().get(entityClass, id);
		return initializeProxy(entity);
	}
 
	/**
	 * Persists an object
	 *
	 * @param entity to be persisted
	 */
	@SuppressWarnings("unchecked")
	public Entity save(Entity entity) {
		getHibernateTemplate().saveOrUpdate(entity);
		return initializeProxy(entity);
	}
 
	/**
	 * Removes an entity from the data store
	 *
	 * @param entity to be removed
	 */
	@SuppressWarnings("unchecked")
	public Entity delete(Entity entity) {
		getHibernateTemplate().delete(entity);
		return initializeProxy(entity);
	}
 
 
	private HibernateTemplate getPreparedHibernateTemplate() {
        HibernateTemplate template = getHibernateTemplate();
        template.setCacheQueries(true);
        template.setFlushMode(HibernateAccessor.FLUSH_AUTO);
        return template;
    }
 
	private Entity initializeProxy(Entity entity) {
		getPreparedHibernateTemplate().initialize(entity);
		return entity;
	}
 
	/**
	 * Finds a class associated with this path name
	 *
	 * @param name the name
	 * @return the found class if any
	 */
	@SuppressWarnings("unchecked")
	public Class<Entity> getClassForName(String name) {
		final String normalizedName = name.length() > 0 ? name.substring(name.lastIndexOf("."),1).toUpperCase() + name.substring(0) : name;
		return (Class<Entity>) getPreparedHibernateTemplate().execute(new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SessionFactory factory = session.getSessionFactory();
				ClassMetadata classMetadata = factory.getClassMetadata(normalizedName);
				return classMetadata != null ? classMetadata.getMappedClass(EntityMode.POJO) : null;
			}
		});
	}
}
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • MySpace
  • RSS
  • TwitThis
  • email
  • FriendFeed
  • PDF
  • Slashdot

This post is tagged , , , , , , , ,

don't '

Leave a Reply

Comments may be held for moderation, please do not repost. I reserve the right to remove any inappropriate or off-topic comments. If you plan on sharing helpful code, please pass it through Postable first. Want other to know who you are? Register a Gravatar.





Sponsored Links

Categories

Pages

mousetrap bar muppets music relocate server share 1965 peace prize recipiant ai wo koete focus macro for warlock 12 steps of 12 step program duchess restaurant in norwalk ct nubian heritage harlem ferry from kos to lesvos american famaly ins approach meeting discuss forward opportunity 2008 topps update highlights yankees bern north carolina alcohol drug halfway houses phoenix az bartender 4 cindysthrows.com 1st global national conference girls yuri rimini coast icu and ethic girls basketball brethren christian high school codes to the legend of zelda association-office.com blood elf priest dry brushing cabinets free needlepoint slipper pattern aaacap.org gluten free sauces cast and crowns butternut squash ravioli chicago 1920 s style shutter hinges butterflies lake morton alabama malamute 1996 jaguar oil pan anita denise fuller 72 listening skills curved chain nose pliers kari byron pics mythbusters afghan and fruit of the spirit a sweet science fair project discourse markers activities for tefl fractal mac os x dexadine.com bass companion cassie cato partingwishes.com bittorrent free downloader mac apostles today boba fet and leia bonsai societies newsletters coupon code dvdxpress kiosk penguin wild tangent crack 12 step bible burroughs farms brighton michigan penske rental in chico california chocolate coconut truffles copy a locked file brick paving driveway efi smithsonian tours ball draining blowjobs aircraft latrine discharge educacion locke benjamin james loves born in tennessee east brunswick nj vo tech email contact adresses ministers in usa yogafamily.com aol always sunny in philadelphia 2020 automotive group az christmas teas at hotels game ideas for family reunions 2x 4 saddle rack florida radon training brinkmann charcoal smoker modification bali hindu hawks ridge duluth minn ace bowling ball bargains.com arizona motto ditat mongolian barbeque denver buy fujitsu scansnap color image scanner .22 stirling rifles blue spiny lizard ancient stone walls in oakland ca cherry tree leaf parasites elderberry shrubs piragis.com hbs and mpc provider dandy vom hause neubrand kg frontierairline.com box sash windows kent wee ride calgon catalytic carbon filters family homestay in kyoto church choir robes 3288 tranquility lane ao calculate multi range requirement beliefs of messianic judaism charlie bit me utube lakers apparel civil-war.com how to get harder erections tartanstore.net deux de coeur download sega saturn emulator defiance movie watching free child to cherish handprint ornament aftermarketsupport.com benjamin m curry saginaw mi cityofphoenix.gov anakin skywalker darth vader bios not acpi compliant big toe manipulation ac schnitzer facelift lip 92 silver eagle coach billy peterson csi abdomen fat getting large pagesvpp.com 108mbps gaming adapter removeable antenna air bake muffin maris carey touch my body bunting bearings ku nudesville.com 02 pt crusier touring edition manual craiglist fargo how may kkk members are there tilley.com caviar yamaha a life of faith dolls outrageous blondes foodsafeschools.org chesapeake general hosp youngsrvcenters.com globe weis folders online news pakistani channel aloe vera ears campgrounds by wildwood beach new jersey 138 highway closure fgcdirect9.com 6.5 crank sensor tomb-raider-anniversary.com alhambra california newspaper bobble fish days of elijah lyrics bk 2160 parts lloyd james wingate susannah cannon camera retailers honduras 2002 mustang gt spark plugs australian chemist ixsweb.com catholic monastery tennessee carpel tunnel syndrome in golfers electoral college federalist papers australian carp record el segundo stick n stein dodge dually big tires ankle fractures in pediatric patients 1975 toyota celica for sale 115th police academy cadets austin texas conspiracy theory robots downloading soul list of common transitional words dr horner in belleville il leech inside urethra artery inflamation cannondale adventure 800