Added petclinic sample

This commit is contained in:
Arjen Poutsma 2008-11-20 13:50:01 +00:00
parent 68dfd9efe8
commit 9d3cb3a47e
98 changed files with 5693 additions and 0 deletions

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="org.springframework.samples.petclinic">
<property file="${basedir}/../build.properties"/>
<import file="${basedir}/../spring-build/war/default.xml"/>
</project>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
<ivy-module
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
version="1.3">
<info organisation="org.springframework" module="${ant.project.name}">
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
</info>
<configurations>
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
<conf name="hibernate" extends="runtime" description="JARs needed to use Hibernate"/>
<conf name="jpa" extends="runtime" description="JARs needed to develop JPA beans"/>
<conf name="toplink-essentials" extends="jpa" description="JARs needed to use TopLink Essentials JPA"/>
</configurations>
<publications>
<artifact name="${ant.project.name}" type="war" ext="war"/>
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
</publications>
<dependencies>
<dependency org="com.oracle.toplink.essentials" name="com.springsource.oracle.toplink.essentials" rev="2.0.0.b41-beta2" conf="optional, toplink-essentials->compile"/>
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="provided, jpa->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.4.0" conf="compile->compile"/>
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->compile"/>
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="compile->compile"/>
<dependency org="org.aspectj" name="com.springsource.org.aspectj.weaver" rev="1.5.4" conf="compile->compile"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.2.6.ga" conf="optional, hibernate->compile"/>
<dependency org="org.springframework" name="org.springframework.orm" rev="latest.integration" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.web.servlet" rev="latest.integration" conf="compile->compile"/>
<!-- test dependencies -->
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
<dependency org="org.springframework" name="org.springframework.test" rev="latest.integration" conf="test->compile"/>
<dependency org="org.objectweb.asm" name="com.springsource.org.objectweb.asm" rev="2.2.3" conf="test->compile" />
<dependency org="org.objectweb.asm" name="com.springsource.org.objectweb.asm.commons" rev="2.2.3" conf="test->compile" />
</dependencies>
</ivy-module>

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<module relativePaths="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="web" name="Web">
<configuration>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/web/WEB-INF/web.xml" optional="false" version="2.5" />
</descriptors>
<webroots>
<root url="file://$MODULE_DIR$/web" relative="/" />
</webroots>
<building>
<setting name="EXPLODED_URL" value="file://$MODULE_DIR$/../out/exploded/petclinicWeb" />
<setting name="EXPLODED_ENABLED" value="true" />
<setting name="JAR_URL" value="file://" />
<setting name="JAR_ENABLED" value="false" />
<setting name="EXCLUDE_EXPLODED_DIRECTORY" value="true" />
</building>
<packaging>
<containerElement type="module" name="petclinic">
<attribute name="method" value="1" />
<attribute name="URI" value="/WEB-INF/classes" />
</containerElement>
</packaging>
</configuration>
</facet>
<facet type="web" name="Web2">
<configuration>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml" optional="false" version="2.5" />
</descriptors>
<webroots>
<root url="file://$MODULE_DIR$/src/main/webapp" relative="/" />
</webroots>
<building>
<setting name="EXPLODED_URL" value="file://" />
<setting name="EXPLODED_ENABLED" value="false" />
<setting name="JAR_URL" value="file://" />
<setting name="JAR_ENABLED" value="false" />
<setting name="EXCLUDE_EXPLODED_DIRECTORY" value="true" />
</building>
<packaging>
<containerElement type="module" name="petclinic">
<attribute name="method" value="1" />
<attribute name="URI" value="/WEB-INF/classes" />
</containerElement>
</packaging>
</configuration>
</facet>
<facet type="jpa" name="JPA">
<configuration>
<setting name="validation-enabled" value="true" />
<setting name="provider-name" value="" />
<setting name="targe-facet" value="" />
<datasource-mapping />
<deploymentDescriptor name="persistence.xml" url="file://$MODULE_DIR$/src/main/resources/META-INF/persistence.xml" optional="false" version="1.0" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$APPLICATION_HOME_DIR$/lib/javaee.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module" module-name="web-servlet" />
<orderEntry type="module" module-name="web" />
<orderEntry type="module" module-name="beans" />
<orderEntry type="module" module-name="transaction" />
<orderEntry type="module" module-name="core" />
<orderEntry type="module" module-name="context" />
<orderEntry type="module" module-name="jdbc" />
<orderEntry type="module" module-name="test" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.junit/com.springsource.org.junit/4.4.0/com.springsource.org.junit-4.4.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.junit/com.springsource.org.junit/4.4.0/com.springsource.org.junit-sources-4.4.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module" module-name="orm" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.aspectj/com.springsource.org.aspectj.weaver/1.5.4/com.springsource.org.aspectj.weaver-1.5.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/com.oracle.toplink.essentials/com.springsource.oracle.toplink.essentials/2.0.0.b41-beta2/com.springsource.oracle.toplink.essentials-2.0.0.b41-beta2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/com.oracle.toplink.essentials/com.springsource.oracle.toplink.essentials/2.0.0.b41-beta2/com.springsource.oracle.toplink.essentials-sources-2.0.0.b41-beta2.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.2.6.ga/com.springsource.org.hibernate-3.2.6.ga.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.2.6.ga/com.springsource.org.hibernate-sources-3.2.6.ga.jar!/" />
</SOURCES>
</library>
</orderEntry>
</component>
</module>

View File

@ -0,0 +1,27 @@
package org.springframework.samples.petclinic;
/**
* Simple JavaBean domain object with an id property.
* Used as a base class for objects needing this property.
*
* @author Ken Krebs
* @author Juergen Hoeller
*/
public class BaseEntity {
private Integer id;
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public boolean isNew() {
return (this.id == null);
}
}

View File

@ -0,0 +1,77 @@
package org.springframework.samples.petclinic;
import java.util.Collection;
import org.springframework.dao.DataAccessException;
/**
* The high-level PetClinic business interface.
*
* <p>This is basically a data access object.
* PetClinic doesn't have a dedicated business facade.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
*/
public interface Clinic {
/**
* Retrieve all <code>Vet</code>s from the data store.
* @return a <code>Collection</code> of <code>Vet</code>s
*/
Collection<Vet> getVets() throws DataAccessException;
/**
* Retrieve all <code>PetType</code>s from the data store.
* @return a <code>Collection</code> of <code>PetType</code>s
*/
Collection<PetType> getPetTypes() throws DataAccessException;
/**
* Retrieve <code>Owner</code>s from the data store by last name,
* returning all owners whose last name <i>starts</i> with the given name.
* @param lastName Value to search for
* @return a <code>Collection</code> of matching <code>Owner</code>s
* (or an empty <code>Collection</code> if none found)
*/
Collection<Owner> findOwners(String lastName) throws DataAccessException;
/**
* Retrieve an <code>Owner</code> from the data store by id.
* @param id the id to search for
* @return the <code>Owner</code> if found
* @throws org.springframework.dao.DataRetrievalFailureException if not found
*/
Owner loadOwner(int id) throws DataAccessException;
/**
* Retrieve a <code>Pet</code> from the data store by id.
* @param id the id to search for
* @return the <code>Pet</code> if found
* @throws org.springframework.dao.DataRetrievalFailureException if not found
*/
Pet loadPet(int id) throws DataAccessException;
/**
* Save an <code>Owner</code> to the data store, either inserting or updating it.
* @param owner the <code>Owner</code> to save
* @see BaseEntity#isNew
*/
void storeOwner(Owner owner) throws DataAccessException;
/**
* Save a <code>Pet</code> to the data store, either inserting or updating it.
* @param pet the <code>Pet</code> to save
* @see BaseEntity#isNew
*/
void storePet(Pet pet) throws DataAccessException;
/**
* Save a <code>Visit</code> to the data store, either inserting or updating it.
* @param visit the <code>Visit</code> to save
* @see BaseEntity#isNew
*/
void storeVisit(Visit visit) throws DataAccessException;
}

View File

@ -0,0 +1,28 @@
package org.springframework.samples.petclinic;
/**
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>.
* Used as a base class for objects needing these properties.
*
* @author Ken Krebs
* @author Juergen Hoeller
*/
public class NamedEntity extends BaseEntity {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
@Override
public String toString() {
return this.getName();
}
}

View File

@ -0,0 +1,127 @@
package org.springframework.samples.petclinic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.core.style.ToStringCreator;
/**
* Simple JavaBean domain object representing an owner.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class Owner extends Person {
private String address;
private String city;
private String telephone;
private Set<Pet> pets;
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return this.city;
}
public void setCity(String city) {
this.city = city;
}
public String getTelephone() {
return this.telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
protected void setPetsInternal(Set<Pet> pets) {
this.pets = pets;
}
protected Set<Pet> getPetsInternal() {
if (this.pets == null) {
this.pets = new HashSet<Pet>();
}
return this.pets;
}
public List<Pet> getPets() {
List<Pet> sortedPets = new ArrayList<Pet>(getPetsInternal());
PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedPets);
}
public void addPet(Pet pet) {
getPetsInternal().add(pet);
pet.setOwner(this);
}
/**
* Return the Pet with the given name, or null if none found for this Owner.
*
* @param name to test
* @return true if pet name is already in use
*/
public Pet getPet(String name) {
return getPet(name, false);
}
/**
* Return the Pet with the given name, or null if none found for this Owner.
*
* @param name to test
* @return true if pet name is already in use
*/
public Pet getPet(String name, boolean ignoreNew) {
name = name.toLowerCase();
for (Pet pet : getPetsInternal()) {
if (!ignoreNew || !pet.isNew()) {
String compName = pet.getName();
compName = compName.toLowerCase();
if (compName.equals(name)) {
return pet;
}
}
}
return null;
}
@Override
public String toString() {
return new ToStringCreator(this)
.append("id", this.getId())
.append("new", this.isNew())
.append("lastName", this.getLastName())
.append("firstName", this.getFirstName())
.append("address", this.address)
.append("city", this.city)
.append("telephone", this.telephone)
.toString();
}
}

View File

@ -0,0 +1,32 @@
package org.springframework.samples.petclinic;
/**
* Simple JavaBean domain object representing an person.
*
* @author Ken Krebs
*/
public class Person extends BaseEntity {
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

View File

@ -0,0 +1,77 @@
package org.springframework.samples.petclinic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
/**
* Simple JavaBean business object representing a pet.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class Pet extends NamedEntity {
private Date birthDate;
private PetType type;
private Owner owner;
private Set<Visit> visits;
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public Date getBirthDate() {
return this.birthDate;
}
public void setType(PetType type) {
this.type = type;
}
public PetType getType() {
return this.type;
}
protected void setOwner(Owner owner) {
this.owner = owner;
}
public Owner getOwner() {
return this.owner;
}
protected void setVisitsInternal(Set<Visit> visits) {
this.visits = visits;
}
protected Set<Visit> getVisitsInternal() {
if (this.visits == null) {
this.visits = new HashSet<Visit>();
}
return this.visits;
}
public List<Visit> getVisits() {
List<Visit> sortedVisits = new ArrayList<Visit>(getVisitsInternal());
PropertyComparator.sort(sortedVisits, new MutableSortDefinition("date", false, false));
return Collections.unmodifiableList(sortedVisits);
}
public void addVisit(Visit visit) {
getVisitsInternal().add(visit);
visit.setPet(this);
}
}

View File

@ -0,0 +1,8 @@
package org.springframework.samples.petclinic;
/**
* @author Juergen Hoeller
*/
public class PetType extends NamedEntity {
}

View File

@ -0,0 +1,10 @@
package org.springframework.samples.petclinic;
/**
* Models a {@link Vet Vet's} specialty (for example, dentistry).
*
* @author Juergen Hoeller
*/
public class Specialty extends NamedEntity {
}

View File

@ -0,0 +1,49 @@
package org.springframework.samples.petclinic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
/**
* Simple JavaBean domain object representing a veterinarian.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class Vet extends Person {
private Set<Specialty> specialties;
protected void setSpecialtiesInternal(Set<Specialty> specialties) {
this.specialties = specialties;
}
protected Set<Specialty> getSpecialtiesInternal() {
if (this.specialties == null) {
this.specialties = new HashSet<Specialty>();
}
return this.specialties;
}
public List<Specialty> getSpecialties() {
List<Specialty> sortedSpecs = new ArrayList<Specialty>(getSpecialtiesInternal());
PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedSpecs);
}
public int getNrOfSpecialties() {
return getSpecialtiesInternal().size();
}
public void addSpecialty(Specialty specialty) {
getSpecialtiesInternal().add(specialty);
}
}

View File

@ -0,0 +1,70 @@
package org.springframework.samples.petclinic;
import java.util.Date;
/**
* Simple JavaBean domain object representing a visit.
*
* @author Ken Krebs
*/
public class Visit extends BaseEntity {
/** Holds value of property date. */
private Date date;
/** Holds value of property description. */
private String description;
/** Holds value of property pet. */
private Pet pet;
/** Creates a new instance of Visit for the current date */
public Visit() {
this.date = new Date();
}
/** Getter for property date.
* @return Value of property date.
*/
public Date getDate() {
return this.date;
}
/** Setter for property date.
* @param date New value of property date.
*/
public void setDate(Date date) {
this.date = date;
}
/** Getter for property description.
* @return Value of property description.
*/
public String getDescription() {
return this.description;
}
/** Setter for property description.
* @param description New value of property description.
*/
public void setDescription(String description) {
this.description = description;
}
/** Getter for property pet.
* @return Value of property pet.
*/
public Pet getPet() {
return this.pet;
}
/** Setter for property pet.
* @param pet New value of property pet.
*/
protected void setPet(Pet pet) {
this.pet = pet;
}
}

View File

@ -0,0 +1,31 @@
package org.springframework.samples.petclinic.aspects;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* Aspect to illustrate Spring-driven load-time weaving.
*
* @author Ramnivas Laddad
* @since 2.5
*/
@Aspect
public abstract class AbstractTraceAspect {
private static final Log logger = LogFactory.getLog(AbstractTraceAspect.class);
@Pointcut
public abstract void traced();
@Before("traced()")
public void trace(JoinPoint.StaticPart jpsp) {
if (logger.isTraceEnabled()) {
logger.trace("Entering " + jpsp.getSignature().toLongString());
}
}
}

View File

@ -0,0 +1,81 @@
package org.springframework.samples.petclinic.aspects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.StopWatch;
/**
* Simple AspectJ aspect that monitors call count and call invocation time.
* Implements the CallMonitor management interface.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.5
*/
@ManagedResource("petclinic:type=CallMonitor")
@Aspect
public class CallMonitoringAspect {
private boolean isEnabled = true;
private int callCount = 0;
private long accumulatedCallTime = 0;
@ManagedAttribute
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
@ManagedAttribute
public boolean isEnabled() {
return isEnabled;
}
@ManagedOperation
public void reset() {
this.callCount = 0;
this.accumulatedCallTime = 0;
}
@ManagedAttribute
public int getCallCount() {
return callCount;
}
@ManagedAttribute
public long getCallTime() {
return (this.callCount > 0 ? this.accumulatedCallTime / this.callCount : 0);
}
@Around("within(@org.springframework.stereotype.Service *)")
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
if (this.isEnabled) {
StopWatch sw = new StopWatch(joinPoint.toShortString());
sw.start("invoke");
try {
return joinPoint.proceed();
}
finally {
sw.stop();
synchronized (this) {
this.callCount++;
this.accumulatedCallTime += sw.getTotalTimeMillis();
}
}
}
else {
return joinPoint.proceed();
}
}
}

View File

@ -0,0 +1,48 @@
package org.springframework.samples.petclinic.aspects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* Sample AspectJ annotation-style aspect that saves
* every owner name requested to the clinic.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
@Aspect
public class UsageLogAspect {
private int historySize = 100;
// Of course saving all names is not suitable for
// production use, but this is a simple example.
private List<String> namesRequested = new ArrayList<String>(this.historySize);
public synchronized void setHistorySize(int historySize) {
this.historySize = historySize;
this.namesRequested = new ArrayList<String>(historySize);
}
@Before("execution(* *.findOwners(String)) && args(name)")
public synchronized void logNameRequest(String name) {
// Not the most efficient implementation,
// but we're aiming to illustrate the power of
// @AspectJ AOP, not write perfect code here :-)
if (this.namesRequested.size() > this.historySize) {
this.namesRequested.remove(0);
}
this.namesRequested.add(name);
}
public synchronized List<String> getNamesRequested() {
return Collections.unmodifiableList(this.namesRequested);
}
}

View File

@ -0,0 +1,89 @@
package org.springframework.samples.petclinic.hibernate;
import java.util.Collection;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.Vet;
import org.springframework.samples.petclinic.Visit;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* Hibernate implementation of the Clinic interface.
*
* <p>The mappings are defined in "petclinic.hbm.xml", located in the root of the
* class path.
*
* <p>Note that transactions are declared with annotations and that some methods
* contain "readOnly = true" which is an optimization that is particularly
* valuable when using Hibernate (to suppress unnecessary flush attempts for
* read-only operations).
*
* @author Juergen Hoeller
* @author Sam Brannen
* @author Mark Fisher
* @since 19.10.2003
*/
@Repository
@Transactional
public class HibernateClinic implements Clinic {
@Autowired
private SessionFactory sessionFactory;
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Vet> getVets() {
return sessionFactory.getCurrentSession().createQuery("from Vet vet order by vet.lastName, vet.firstName").list();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<PetType> getPetTypes() {
return sessionFactory.getCurrentSession().createQuery("from PetType type order by type.name").list();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Owner> findOwners(String lastName) {
return sessionFactory.getCurrentSession().createQuery("from Owner owner where owner.lastName like :lastName")
.setString("lastName", lastName + "%").list();
}
@Transactional(readOnly = true)
public Owner loadOwner(int id) {
return (Owner) sessionFactory.getCurrentSession().load(Owner.class, id);
}
@Transactional(readOnly = true)
public Pet loadPet(int id) {
return (Pet) sessionFactory.getCurrentSession().load(Pet.class, id);
}
public void storeOwner(Owner owner) {
// Note: Hibernate3's merge operation does not reassociate the object
// with the current Hibernate Session. Instead, it will always copy the
// state over to a registered representation of the entity. In case of a
// new entity, it will register a copy as well, but will not update the
// id of the passed-in object. To still update the ids of the original
// objects too, we need to register Spring's
// IdTransferringMergeEventListener on our SessionFactory.
sessionFactory.getCurrentSession().merge(owner);
}
public void storePet(Pet pet) {
sessionFactory.getCurrentSession().merge(pet);
}
public void storeVisit(Visit visit) {
sessionFactory.getCurrentSession().merge(visit);
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
The classes in this package represent the Hibernate implementation
of PetClinic's persistence layer.
</body>
</html>

View File

@ -0,0 +1,35 @@
package org.springframework.samples.petclinic.jdbc;
import org.springframework.samples.petclinic.Pet;
/**
* Subclass of Pet that carries temporary id properties which
* are only relevant for a JDBC implmentation of the Clinic.
*
* @author Juergen Hoeller
* @see SimpleJdbcClinic
*/
class JdbcPet extends Pet {
private int typeId;
private int ownerId;
public void setTypeId(int typeId) {
this.typeId = typeId;
}
public int getTypeId() {
return this.typeId;
}
public void setOwnerId(int ownerId) {
this.ownerId = ownerId;
}
public int getOwnerId() {
return this.ownerId;
}
}

View File

@ -0,0 +1,338 @@
package org.springframework.samples.petclinic.jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.Specialty;
import org.springframework.samples.petclinic.Vet;
import org.springframework.samples.petclinic.Visit;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* A simple JDBC-based implementation of the {@link Clinic} interface.
*
* <p>This class uses Java 5 language features and the {@link SimpleJdbcTemplate}
* plus {@link SimpleJdbcInsert}. It also takes advantage of classes like
* {@link BeanPropertySqlParameterSource} and
* {@link ParameterizedBeanPropertyRowMapper} which provide automatic mapping
* between JavaBean properties and JDBC parameters or query results.
*
* <p>SimpleJdbcClinic is a rewrite of the AbstractJdbcClinic which was the base
* class for JDBC implementations of the Clinic interface for Spring 2.0.
*
* @author Ken Krebs
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Thomas Risberg
* @author Mark Fisher
*/
@Service
@ManagedResource("petclinic:type=Clinic")
public class SimpleJdbcClinic implements Clinic, SimpleJdbcClinicMBean {
private final Log logger = LogFactory.getLog(getClass());
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert insertOwner;
private SimpleJdbcInsert insertPet;
private SimpleJdbcInsert insertVisit;
private final List<Vet> vets = new ArrayList<Vet>();
@Autowired
public void init(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.insertOwner = new SimpleJdbcInsert(dataSource)
.withTableName("owners")
.usingGeneratedKeyColumns("id");
this.insertPet = new SimpleJdbcInsert(dataSource)
.withTableName("pets")
.usingGeneratedKeyColumns("id");
this.insertVisit = new SimpleJdbcInsert(dataSource)
.withTableName("visits")
.usingGeneratedKeyColumns("id");
}
/**
* Refresh the cache of Vets that the Clinic is holding.
* @see org.springframework.samples.petclinic.Clinic#getVets()
*/
@ManagedOperation
@Transactional(readOnly = true)
public void refreshVetsCache() throws DataAccessException {
synchronized (this.vets) {
this.logger.info("Refreshing vets cache");
// Retrieve the list of all vets.
this.vets.clear();
this.vets.addAll(this.simpleJdbcTemplate.query(
"SELECT id, first_name, last_name FROM vets ORDER BY last_name,first_name",
ParameterizedBeanPropertyRowMapper.newInstance(Vet.class)));
// Retrieve the list of all possible specialties.
final List<Specialty> specialties = this.simpleJdbcTemplate.query(
"SELECT id, name FROM specialties",
ParameterizedBeanPropertyRowMapper.newInstance(Specialty.class));
// Build each vet's list of specialties.
for (Vet vet : this.vets) {
final List<Integer> vetSpecialtiesIds = this.simpleJdbcTemplate.query(
"SELECT specialty_id FROM vet_specialties WHERE vet_id=?",
new ParameterizedRowMapper<Integer>() {
public Integer mapRow(ResultSet rs, int row) throws SQLException {
return Integer.valueOf(rs.getInt(1));
}},
vet.getId().intValue());
for (int specialtyId : vetSpecialtiesIds) {
Specialty specialty = EntityUtils.getById(specialties, Specialty.class, specialtyId);
vet.addSpecialty(specialty);
}
}
}
}
// START of Clinic implementation section *******************************
@Transactional(readOnly = true)
public Collection<Vet> getVets() throws DataAccessException {
synchronized (this.vets) {
if (this.vets.isEmpty()) {
refreshVetsCache();
}
return this.vets;
}
}
@Transactional(readOnly = true)
public Collection<PetType> getPetTypes() throws DataAccessException {
return this.simpleJdbcTemplate.query(
"SELECT id, name FROM types ORDER BY name",
ParameterizedBeanPropertyRowMapper.newInstance(PetType.class));
}
/**
* Loads {@link Owner Owners} from the data store by last name, returning
* all owners whose last name <i>starts</i> with the given name; also loads
* the {@link Pet Pets} and {@link Visit Visits} for the corresponding
* owners, if not already loaded.
*/
@Transactional(readOnly = true)
public Collection<Owner> findOwners(String lastName) throws DataAccessException {
List<Owner> owners = this.simpleJdbcTemplate.query(
"SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE last_name like ?",
ParameterizedBeanPropertyRowMapper.newInstance(Owner.class),
lastName + "%");
loadOwnersPetsAndVisits(owners);
return owners;
}
/**
* Loads the {@link Owner} with the supplied <code>id</code>; also loads
* the {@link Pet Pets} and {@link Visit Visits} for the corresponding
* owner, if not already loaded.
*/
@Transactional(readOnly = true)
public Owner loadOwner(int id) throws DataAccessException {
Owner owner;
try {
owner = this.simpleJdbcTemplate.queryForObject(
"SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE id=?",
ParameterizedBeanPropertyRowMapper.newInstance(Owner.class),
id);
}
catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Owner.class, new Integer(id));
}
loadPetsAndVisits(owner);
return owner;
}
@Transactional(readOnly = true)
public Pet loadPet(int id) throws DataAccessException {
JdbcPet pet;
try {
pet = this.simpleJdbcTemplate.queryForObject(
"SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE id=?",
new JdbcPetRowMapper(),
id);
}
catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Pet.class, new Integer(id));
}
Owner owner = loadOwner(pet.getOwnerId());
owner.addPet(pet);
pet.setType(EntityUtils.getById(getPetTypes(), PetType.class, pet.getTypeId()));
loadVisits(pet);
return pet;
}
@Transactional
public void storeOwner(Owner owner) throws DataAccessException {
if (owner.isNew()) {
Number newKey = this.insertOwner.executeAndReturnKey(
new BeanPropertySqlParameterSource(owner));
owner.setId(newKey.intValue());
}
else {
this.simpleJdbcTemplate.update(
"UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " +
"city=:city, telephone=:telephone WHERE id=:id",
new BeanPropertySqlParameterSource(owner));
}
}
@Transactional
public void storePet(Pet pet) throws DataAccessException {
if (pet.isNew()) {
Number newKey = this.insertPet.executeAndReturnKey(
createPetParameterSource(pet));
pet.setId(newKey.intValue());
}
else {
this.simpleJdbcTemplate.update(
"UPDATE pets SET name=:name, birth_date=:birth_date, type_id=:type_id, " +
"owner_id=:owner_id WHERE id=:id",
createPetParameterSource(pet));
}
}
@Transactional
public void storeVisit(Visit visit) throws DataAccessException {
if (visit.isNew()) {
Number newKey = this.insertVisit.executeAndReturnKey(
createVisitParameterSource(visit));
visit.setId(newKey.intValue());
}
else {
throw new UnsupportedOperationException("Visit update not supported");
}
}
// END of Clinic implementation section ************************************
/**
* Creates a {@link MapSqlParameterSource} based on data values from the
* supplied {@link Pet} instance.
*/
private MapSqlParameterSource createPetParameterSource(Pet pet) {
return new MapSqlParameterSource()
.addValue("id", pet.getId())
.addValue("name", pet.getName())
.addValue("birth_date", pet.getBirthDate())
.addValue("type_id", pet.getType().getId())
.addValue("owner_id", pet.getOwner().getId());
}
/**
* Creates a {@link MapSqlParameterSource} based on data values from the
* supplied {@link Visit} instance.
*/
private MapSqlParameterSource createVisitParameterSource(Visit visit) {
return new MapSqlParameterSource()
.addValue("id", visit.getId())
.addValue("visit_date", visit.getDate())
.addValue("description", visit.getDescription())
.addValue("pet_id", visit.getPet().getId());
}
/**
* Loads the {@link Visit} data for the supplied {@link Pet}.
*/
private void loadVisits(JdbcPet pet) {
final List<Visit> visits = this.simpleJdbcTemplate.query(
"SELECT id, visit_date, description FROM visits WHERE pet_id=?",
new ParameterizedRowMapper<Visit>() {
public Visit mapRow(ResultSet rs, int row) throws SQLException {
Visit visit = new Visit();
visit.setId(rs.getInt("id"));
visit.setDate(rs.getTimestamp("visit_date"));
visit.setDescription(rs.getString("description"));
return visit;
}
},
pet.getId().intValue());
for (Visit visit : visits) {
pet.addVisit(visit);
}
}
/**
* Loads the {@link Pet} and {@link Visit} data for the supplied
* {@link Owner}.
*/
private void loadPetsAndVisits(final Owner owner) {
final List<JdbcPet> pets = this.simpleJdbcTemplate.query(
"SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE owner_id=?",
new JdbcPetRowMapper(),
owner.getId().intValue());
for (JdbcPet pet : pets) {
owner.addPet(pet);
pet.setType(EntityUtils.getById(getPetTypes(), PetType.class, pet.getTypeId()));
loadVisits(pet);
}
}
/**
* Loads the {@link Pet} and {@link Visit} data for the supplied
* {@link List} of {@link Owner Owners}.
*
* @param owners the list of owners for whom the pet and visit data should be loaded
* @see #loadPetsAndVisits(Owner)
*/
private void loadOwnersPetsAndVisits(List<Owner> owners) {
for (Owner owner : owners) {
loadPetsAndVisits(owner);
}
}
/**
* {@link ParameterizedRowMapper} implementation mapping data from a
* {@link ResultSet} to the corresponding properties of the {@link JdbcPet} class.
*/
private class JdbcPetRowMapper implements ParameterizedRowMapper<JdbcPet> {
public JdbcPet mapRow(ResultSet rs, int rownum) throws SQLException {
JdbcPet pet = new JdbcPet();
pet.setId(rs.getInt("id"));
pet.setName(rs.getString("name"));
pet.setBirthDate(rs.getDate("birth_date"));
pet.setTypeId(rs.getInt("type_id"));
pet.setOwnerId(rs.getInt("owner_id"));
return pet;
}
}
}

View File

@ -0,0 +1,20 @@
package org.springframework.samples.petclinic.jdbc;
/**
* Interface that defines a cache refresh operation.
* To be exposed for management via JMX.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @see SimpleJdbcClinic
*/
public interface SimpleJdbcClinicMBean {
/**
* Refresh the cache of Vets that the Clinic is holding.
* @see org.springframework.samples.petclinic.Clinic#getVets()
* @see SimpleJdbcClinic#refreshVetsCache()
*/
void refreshVetsCache();
}

View File

@ -0,0 +1,8 @@
<html>
<body>
The classes in this package represent the JDBC implementation
of PetClinic's persistence layer.
</body>
</html>

View File

@ -0,0 +1,90 @@
package org.springframework.samples.petclinic.jpa;
import java.util.Collection;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.Vet;
import org.springframework.samples.petclinic.Visit;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* JPA implementation of the Clinic interface using EntityManager.
*
* <p>The mappings are defined in "orm.xml" located in the META-INF directory.
*
* @author Mike Keith
* @author Rod Johnson
* @author Sam Brannen
* @since 22.4.2006
*/
@Repository
@Transactional
public class EntityManagerClinic implements Clinic {
@PersistenceContext
private EntityManager em;
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Vet> getVets() {
return this.em.createQuery("SELECT vet FROM Vet vet ORDER BY vet.lastName, vet.firstName").getResultList();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<PetType> getPetTypes() {
return this.em.createQuery("SELECT ptype FROM PetType ptype ORDER BY ptype.name").getResultList();
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public Collection<Owner> findOwners(String lastName) {
Query query = this.em.createQuery("SELECT owner FROM Owner owner WHERE owner.lastName LIKE :lastName");
query.setParameter("lastName", lastName + "%");
return query.getResultList();
}
@Transactional(readOnly = true)
public Owner loadOwner(int id) {
return this.em.find(Owner.class, id);
}
@Transactional(readOnly = true)
public Pet loadPet(int id) {
return this.em.find(Pet.class, id);
}
public void storeOwner(Owner owner) {
// Consider returning the persistent object here, for exposing
// a newly assigned id using any persistence provider...
Owner merged = this.em.merge(owner);
this.em.flush();
owner.setId(merged.getId());
}
public void storePet(Pet pet) {
// Consider returning the persistent object here, for exposing
// a newly assigned id using any persistence provider...
Pet merged = this.em.merge(pet);
this.em.flush();
pet.setId(merged.getId());
}
public void storeVisit(Visit visit) {
// Consider returning the persistent object here, for exposing
// a newly assigned id using any persistence provider...
Visit merged = this.em.merge(visit);
this.em.flush();
visit.setId(merged.getId());
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
The classes in this package represent the JPA implementation
of PetClinic's persistence layer.
</body>
</html>

View File

@ -0,0 +1,7 @@
<html>
<body>
The classes in this package represent PetClinic's business layer.
</body>
</html>

View File

@ -0,0 +1,56 @@
package org.springframework.samples.petclinic.toplink;
import java.io.IOException;
import java.io.Writer;
import oracle.toplink.essentials.exceptions.ValidationException;
import oracle.toplink.essentials.platform.database.HSQLPlatform;
import oracle.toplink.essentials.queryframework.ValueReadQuery;
/**
* Subclass of the TopLink Essentials default HSQLPlatform class, using native
* HSQLDB identity columns for id generation.
*
* <p>Necessary for PetClinic's default data model, which relies on identity
* columns: this is uniformly used across all persistence layer implementations
* (JDBC, Hibernate, and JPA).
*
* @author Juergen Hoeller
* @author <a href="mailto:james.x.clark@oracle.com">James Clark</a>
* @since 1.2
*/
public class EssentialsHSQLPlatformWithNativeSequence extends HSQLPlatform {
private static final long serialVersionUID = -55658009691346735L;
public EssentialsHSQLPlatformWithNativeSequence() {
// setUsesNativeSequencing(true);
}
@Override
public boolean supportsNativeSequenceNumbers() {
return true;
}
@Override
public boolean shouldNativeSequenceAcquireValueAfterInsert() {
return true;
}
@Override
public ValueReadQuery buildSelectQueryForNativeSequence() {
return new ValueReadQuery("CALL IDENTITY()");
}
@Override
public void printFieldIdentityClause(Writer writer) throws ValidationException {
try {
writer.write(" IDENTITY");
}
catch (IOException ex) {
throw ValidationException.fileError(ex);
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
The classes in this package provide support for using the TopLink
implementation with PetClinic's EntityManagerClinic.
</body>
</html>

View File

@ -0,0 +1,41 @@
package org.springframework.samples.petclinic.util;
import java.util.Collection;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.BaseEntity;
/**
* Utility methods for handling entities. Separate from the BaseEntity class
* mainly because of dependency on the ORM-associated
* ObjectRetrievalFailureException.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 29.10.2003
* @see org.springframework.samples.petclinic.BaseEntity
*/
public abstract class EntityUtils {
/**
* Look up the entity of the given class with the given id in the given
* collection.
*
* @param entities the collection to search
* @param entityClass the entity class to look up
* @param entityId the entity id to look up
* @return the found entity
* @throws ObjectRetrievalFailureException if the entity was not found
*/
public static <T extends BaseEntity> T getById(Collection<T> entities, Class<T> entityClass, int entityId)
throws ObjectRetrievalFailureException {
for (T entity : entities) {
if (entity.getId().intValue() == entityId && entityClass.isInstance(entity)) {
return entity;
}
}
throw new ObjectRetrievalFailureException(entityClass, new Integer(entityId));
}
}

View File

@ -0,0 +1,43 @@
package org.springframework.samples.petclinic.validation;
import org.springframework.samples.petclinic.Owner;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
/**
* <code>Validator</code> for <code>Owner</code> forms.
*
* @author Ken Krebs
* @author Juergen Hoeller
*/
public class OwnerValidator {
public void validate(Owner owner, Errors errors) {
if (!StringUtils.hasLength(owner.getFirstName())) {
errors.rejectValue("firstName", "required", "required");
}
if (!StringUtils.hasLength(owner.getLastName())) {
errors.rejectValue("lastName", "required", "required");
}
if (!StringUtils.hasLength(owner.getAddress())) {
errors.rejectValue("address", "required", "required");
}
if (!StringUtils.hasLength(owner.getCity())) {
errors.rejectValue("city", "required", "required");
}
String telephone = owner.getTelephone();
if (!StringUtils.hasLength(telephone)) {
errors.rejectValue("telephone", "required", "required");
}
else {
for (int i = 0; i < telephone.length(); ++i) {
if ((Character.isDigit(telephone.charAt(i))) == false) {
errors.rejectValue("telephone", "nonNumeric", "non-numeric");
break;
}
}
}
}
}

View File

@ -0,0 +1,25 @@
package org.springframework.samples.petclinic.validation;
import org.springframework.samples.petclinic.Pet;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
/**
* <code>Validator</code> for <code>Pet</code> forms.
*
* @author Ken Krebs
* @author Juergen Hoeller
*/
public class PetValidator {
public void validate(Pet pet, Errors errors) {
String name = pet.getName();
if (!StringUtils.hasLength(name)) {
errors.rejectValue("name", "required", "required");
}
else if (pet.isNew() && pet.getOwner().getPet(name, true) != null) {
errors.rejectValue("name", "duplicate", "already exists");
}
}
}

View File

@ -0,0 +1,21 @@
package org.springframework.samples.petclinic.validation;
import org.springframework.samples.petclinic.Visit;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
/**
* <code>Validator</code> for <code>Visit</code> forms.
*
* @author Ken Krebs
* @author Juergen Hoeller
*/
public class VisitValidator {
public void validate(Visit visit, Errors errors) {
if (!StringUtils.hasLength(visit.getDescription())) {
errors.rejectValue("description", "required", "required");
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
The classes in this package represent the set of Validator objects
the Business Layer makes available to the Presentation Layer.
</body>
</html>

View File

@ -0,0 +1,62 @@
package org.springframework.samples.petclinic.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.validation.OwnerValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.WebDataBinder;
/**
* JavaBean form controller that is used to add a new <code>Owner</code> to
* the system.
*
* @author Juergen Hoeller
* @author Ken Krebs
*/
@Controller
@RequestMapping("/addOwner.do")
@SessionAttributes(types = Owner.class)
public class AddOwnerForm {
private final Clinic clinic;
@Autowired
public AddOwnerForm(Clinic clinic) {
this.clinic = clinic;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(new String[] {"id"});
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
Owner owner = new Owner();
model.addAttribute(owner);
return "ownerForm";
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {
new OwnerValidator().validate(owner, result);
if (result.hasErrors()) {
return "ownerForm";
}
else {
this.clinic.storeOwner(owner);
status.setComplete();
return "redirect:owner.do?ownerId=" + owner.getId();
}
}
}

View File

@ -0,0 +1,74 @@
package org.springframework.samples.petclinic.web;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.validation.PetValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.WebDataBinder;
/**
* JavaBean form controller that is used to add a new <code>Pet</code> to the
* system.
*
* @author Juergen Hoeller
* @author Ken Krebs
*/
@Controller
@RequestMapping("/addPet.do")
@SessionAttributes("pet")
public class AddPetForm {
private final Clinic clinic;
@Autowired
public AddPetForm(Clinic clinic) {
this.clinic = clinic;
}
@ModelAttribute("types")
public Collection<PetType> populatePetTypes() {
return this.clinic.getPetTypes();
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(new String[] {"id"});
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("ownerId") int ownerId, Model model) {
Owner owner = this.clinic.loadOwner(ownerId);
Pet pet = new Pet();
owner.addPet(pet);
model.addAttribute("pet", pet);
return "petForm";
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
else {
this.clinic.storePet(pet);
status.setComplete();
return "redirect:owner.do?ownerId=" + pet.getOwner().getId();
}
}
}

View File

@ -0,0 +1,66 @@
package org.springframework.samples.petclinic.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.Visit;
import org.springframework.samples.petclinic.validation.VisitValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.WebDataBinder;
/**
* JavaBean form controller that is used to add a new <code>Visit</code> to
* the system.
*
* @author Juergen Hoeller
* @author Ken Krebs
*/
@Controller
@RequestMapping("/addVisit.do")
@SessionAttributes("visit")
public class AddVisitForm {
private final Clinic clinic;
@Autowired
public AddVisitForm(Clinic clinic) {
this.clinic = clinic;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(new String[] {"id"});
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
Visit visit = new Visit();
pet.addVisit(visit);
model.addAttribute("visit", visit);
return "visitForm";
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("visit") Visit visit, BindingResult result, SessionStatus status) {
new VisitValidator().validate(visit, result);
if (result.hasErrors()) {
return "visitForm";
}
else {
this.clinic.storeVisit(visit);
status.setComplete();
return "redirect:owner.do?ownerId=" + visit.getPet().getOwner().getId();
}
}
}

View File

@ -0,0 +1,37 @@
package org.springframework.samples.petclinic.web;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.PetType;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.WebRequest;
/**
* Shared WebBindingInitializer for PetClinic's custom editors.
*
* <p>Alternatively, such init-binder code may be put into
* {@link org.springframework.web.bind.annotation.InitBinder}
* annotated methods on the controller classes themselves.
*
* @author Juergen Hoeller
*/
public class ClinicBindingInitializer implements WebBindingInitializer {
@Autowired
private Clinic clinic;
public void initBinder(WebDataBinder binder, WebRequest request) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
binder.registerCustomEditor(PetType.class, new PetTypeEditor(this.clinic));
}
}

View File

@ -0,0 +1,72 @@
package org.springframework.samples.petclinic.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Annotation-driven <em>MultiActionController</em> that handles all non-form
* URL's.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Ken Krebs
*/
@Controller
public class ClinicController {
private final Clinic clinic;
@Autowired
public ClinicController(Clinic clinic) {
this.clinic = clinic;
}
/**
* Custom handler for the welcome view.
* <p>
* Note that this handler relies on the RequestToViewNameTranslator to
* determine the logical view name based on the request URL: "/welcome.do"
* -&gt; "welcome".
*/
@RequestMapping("/welcome.do")
public void welcomeHandler() {
}
/**
* Custom handler for displaying vets.
* <p>
* Note that this handler returns a plain {@link ModelMap} object instead of
* a ModelAndView, thus leveraging convention-based model attribute names.
* It relies on the RequestToViewNameTranslator to determine the logical
* view name based on the request URL: "/vets.do" -&gt; "vets".
*
* @return a ModelMap with the model attributes for the view
*/
@RequestMapping("/vets.do")
public ModelMap vetsHandler() {
return new ModelMap(this.clinic.getVets());
}
/**
* Custom handler for displaying an owner.
* <p>
* Note that this handler returns a plain {@link ModelMap} object instead of
* a ModelAndView, thus leveraging convention-based model attribute names.
* It relies on the RequestToViewNameTranslator to determine the logical
* view name based on the request URL: "/owner.do" -&gt; "owner".
*
* @param ownerId the ID of the owner to display
* @return a ModelMap with the model attributes for the view
*/
@RequestMapping("/owner.do")
public ModelMap ownerHandler(@RequestParam("ownerId") int ownerId) {
return new ModelMap(this.clinic.loadOwner(ownerId));
}
}

View File

@ -0,0 +1,62 @@
package org.springframework.samples.petclinic.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.validation.OwnerValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.WebDataBinder;
/**
* JavaBean Form controller that is used to edit an existing <code>Owner</code>.
*
* @author Juergen Hoeller
* @author Ken Krebs
*/
@Controller
@RequestMapping("/editOwner.do")
@SessionAttributes(types = Owner.class)
public class EditOwnerForm {
private final Clinic clinic;
@Autowired
public EditOwnerForm(Clinic clinic) {
this.clinic = clinic;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(new String[] {"id"});
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("ownerId") int ownerId, Model model) {
Owner owner = this.clinic.loadOwner(ownerId);
model.addAttribute(owner);
return "ownerForm";
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {
new OwnerValidator().validate(owner, result);
if (result.hasErrors()) {
return "ownerForm";
}
else {
this.clinic.storeOwner(owner);
status.setComplete();
return "redirect:owner.do?ownerId=" + owner.getId();
}
}
}

View File

@ -0,0 +1,70 @@
package org.springframework.samples.petclinic.web;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.validation.PetValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.bind.WebDataBinder;
/**
* JavaBean Form controller that is used to edit an existing <code>Pet</code>.
*
* @author Juergen Hoeller
* @author Ken Krebs
*/
@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {
private final Clinic clinic;
@Autowired
public EditPetForm(Clinic clinic) {
this.clinic = clinic;
}
@ModelAttribute("types")
public Collection<PetType> populatePetTypes() {
return this.clinic.getPetTypes();
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(new String[] {"id"});
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
else {
this.clinic.storePet(pet);
status.setComplete();
return "redirect:owner.do?ownerId=" + pet.getOwner().getId();
}
}
}

View File

@ -0,0 +1,66 @@
package org.springframework.samples.petclinic.web;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.WebDataBinder;
/**
* JavaBean Form controller that is used to search for <code>Owner</code>s by
* last name.
*
* @author Juergen Hoeller
* @author Ken Krebs
*/
@Controller
@RequestMapping("/findOwners.do")
public class FindOwnersForm {
private final Clinic clinic;
@Autowired
public FindOwnersForm(Clinic clinic) {
this.clinic = clinic;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(new String[] {"id"});
}
@RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
model.addAttribute("owner", new Owner());
return "findOwners";
}
@RequestMapping(method = RequestMethod.POST)
public String processSubmit(Owner owner, BindingResult result, Model model) {
// find owners by last name
Collection<Owner> results = this.clinic.findOwners(owner.getLastName());
if (results.size() < 1) {
// no owners found
result.rejectValue("lastName", "notFound", "not found");
return "findOwners";
}
if (results.size() > 1) {
// multiple owners found
model.addAttribute("selections", results);
return "owners";
}
else {
// 1 owner found
owner = results.iterator().next();
return "redirect:owner.do?ownerId=" + owner.getId();
}
}
}

View File

@ -0,0 +1,30 @@
package org.springframework.samples.petclinic.web;
import java.beans.PropertyEditorSupport;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.PetType;
/**
* @author Mark Fisher
* @author Juergen Hoeller
*/
public class PetTypeEditor extends PropertyEditorSupport {
private final Clinic clinic;
public PetTypeEditor(Clinic clinic) {
this.clinic = clinic;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
for (PetType type : this.clinic.getPetTypes()) {
if (type.getName().equals(text)) {
setValue(type);
}
}
}
}

View File

@ -0,0 +1,7 @@
<html>
<body>
The classes in this package represent PetClinic's web presentation layer.
</body>
</html>

View File

@ -0,0 +1,7 @@
<html>
<body>
<p>
The Spring Data Binding framework, an internal library used by Spring Web Flow.
</p>
</body>
</html>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!-- Custom aspects for the PetClinic sample application -->
<aspectj>
<weaver>
<include within="org.springframework.samples.petclinic..*"/>
</weaver>
<aspects>
<aspect name="org.springframework.samples.petclinic.aspects.UsageLogAspect"/>
<concrete-aspect name="org.springframework.samples.petclinic.aspects.ApplicationTraceAspect"
extends="org.springframework.samples.petclinic.aspects.AbstractTraceAspect">
<pointcut name="traced" expression="execution(* org.springframework.samples..*.*(..))"/>
</concrete-aspect>
</aspects>
</aspectj>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<persistence-unit-metadata>
<xml-mapping-metadata-complete/>
<persistence-unit-defaults>
<access>PROPERTY</access>
</persistence-unit-defaults>
</persistence-unit-metadata>
<package>org.springframework.samples.petclinic</package>
<mapped-superclass class="BaseEntity">
<attributes>
<id name="id">
<generated-value strategy="IDENTITY"/>
</id>
<transient name="new"/>
</attributes>
</mapped-superclass>
<mapped-superclass class="NamedEntity">
<attributes>
<basic name="name">
<column name="NAME"/>
</basic>
</attributes>
</mapped-superclass>
<mapped-superclass class="Person">
<attributes>
<basic name="firstName">
<column name="FIRST_NAME"/>
</basic>
<basic name="lastName">
<column name="LAST_NAME"/>
</basic>
</attributes>
</mapped-superclass>
<entity class="Vet">
<table name="VETS"/>
<attributes>
<many-to-many name="specialtiesInternal" target-entity="Specialty" fetch="EAGER">
<join-table name="VET_SPECIALTIES">
<join-column name="VET_ID"/>
<inverse-join-column name="SPECIALTY_ID"/>
</join-table>
</many-to-many>
<transient name="specialties"/>
<transient name="nrOfSpecialties"/>
</attributes>
</entity>
<entity class="Specialty">
<table name="SPECIALTIES"/>
</entity>
<entity class="Owner">
<table name="OWNERS"/>
<attributes>
<basic name="address"/>
<basic name="city"/>
<basic name="telephone"/>
<one-to-many name="petsInternal" target-entity="Pet" mapped-by="owner" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
<transient name="pets"/>
</attributes>
</entity>
<entity class="Pet">
<table name="PETS"/>
<attributes>
<basic name="birthDate">
<column name="BIRTH_DATE"/>
<temporal>DATE</temporal>
</basic>
<many-to-one name="owner" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
<many-to-one name="type" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
<one-to-many name="visitsInternal" target-entity="Visit" mapped-by="pet" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</one-to-many>
<transient name="visits"/>
</attributes>
</entity>
<entity class="PetType">
<table name="TYPES"/>
</entity>
<entity class="Visit">
<table name="VISITS"/>
<attributes>
<basic name="date">
<column name="VISIT_DATE"/>
<temporal>DATE</temporal>
</basic>
<many-to-one name="pet" fetch="EAGER">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
</attributes>
</entity>
</entity-mappings>

View File

@ -0,0 +1,16 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="PetClinic" transaction-type="RESOURCE_LOCAL">
<!-- Explicitly define mapping file path, else Hibernate won't find the default -->
<mapping-file>META-INF/orm.xml</mapping-file>
<!-- Prevent annotation scanning. In this app we are purely driven by orm.xml -->
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>

View File

@ -0,0 +1,48 @@
# Properties file with JDBC and JPA settings.
#
# Applied by <context:property-placeholder location="jdbc.properties"/> from
# various application context XML files (e.g., "applicationContext-*.xml").
# Targeted at system administrators, to avoid touching the context XML files.
#-------------------------------------------------------------------------------
# Common Settings
hibernate.generate_statistics=true
hibernate.show_sql=true
jpa.showSql=true
#-------------------------------------------------------------------------------
# HSQL Settings
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://localhost:9001
jdbc.username=sa
jdbc.password=
# Property that determines which Hibernate dialect to use
# (only applied with "applicationContext-hibernate.xml")
hibernate.dialect=org.hibernate.dialect.HSQLDialect
# Property that determines which JPA DatabasePlatform to use with TopLink Essentials
jpa.databasePlatform=org.springframework.samples.petclinic.toplink.EssentialsHSQLPlatformWithNativeSequence
# Property that determines which database to use with an AbstractJpaVendorAdapter
jpa.database=HSQL
#-------------------------------------------------------------------------------
# MySQL Settings
#jdbc.driverClassName=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://localhost:3306/petclinic
#jdbc.username=pc
#jdbc.password=pc
# Property that determines which Hibernate dialect to use
# (only applied with "applicationContext-hibernate.xml")
#hibernate.dialect=org.hibernate.dialect.MySQLDialect
# Property that determines which JPA DatabasePlatform to use with TopLink Essentials
#jpa.databasePlatform=oracle.toplink.essentials.platform.database.MySQL4Platform
# Property that determines which database to use with an AbstractJpaVendorAdapter
#jpa.database=MYSQL

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
- Mapping file for the Hibernate implementation of the Clinic interface.
-->
<hibernate-mapping auto-import="true" default-lazy="false">
<class name="org.springframework.samples.petclinic.Vet" table="vets">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<set name="specialtiesInternal" table="vet_specialties">
<key column="vet_id"/>
<many-to-many column="specialty_id" class="org.springframework.samples.petclinic.Specialty"/>
</set>
</class>
<class name="org.springframework.samples.petclinic.Specialty" table="specialties">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
</class>
<class name="org.springframework.samples.petclinic.Owner" table="owners">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<property name="address" column="address"/>
<property name="city" column="city"/>
<property name="telephone" column="telephone"/>
<set name="petsInternal" inverse="true" cascade="all">
<key column="owner_id"/>
<one-to-many class="org.springframework.samples.petclinic.Pet"/>
</set>
</class>
<class name="org.springframework.samples.petclinic.Pet" table="pets">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
<property name="birthDate" column="birth_date" type="date"/>
<many-to-one name="owner" column="owner_id" class="org.springframework.samples.petclinic.Owner"/>
<many-to-one name="type" column="type_id" class="org.springframework.samples.petclinic.PetType"/>
<set name="visitsInternal" inverse="true" cascade="all">
<key column="pet_id"/>
<one-to-many class="org.springframework.samples.petclinic.Visit"/>
</set>
</class>
<class name="org.springframework.samples.petclinic.PetType" table="types">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" column="name"/>
</class>
<class name="org.springframework.samples.petclinic.Visit" table="visits">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="date" column="visit_date" type="date"/>
<property name="description" column="description"/>
<many-to-one name="pet" column="pet_id" class="org.springframework.samples.petclinic.Pet"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,7 @@
<!-- Tomcat context descriptor used for specifying a custom ClassLoader -->
<Context path="/petclinic" reloadable="false">
<!-- please note that useSystemClassLoaderAsParent is available since Tomcat 5.5.20 / remove if previous versions are being used -->
<!--
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/>
-->
</Context>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Application context definition for PetClinic on Hibernate.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
Uses Apache Commons DBCP for connection pooling. See Commons DBCP documentation
for the required JAR files. Alternatively you can use another connection pool
such as C3P0, similarly configured using Spring.
-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
p:dataSource-ref="dataSource" p:mappingResources="petclinic.hbm.xml">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry key="merge">
<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
</entry>
</map>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<!-- Transaction manager that delegates to JTA (for a transactional JNDI DataSource) -->
<!--
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
-->
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes:
Spring's @Required and @Autowired, as well as JSR 250's @Resource.
-->
<context:annotation-config/>
<!--
Instruct Spring to perform declarative transaction management
automatically on annotated classes.
-->
<tx:annotation-driven/>
<!--
Exporter that exposes the Hibernate statistics service via JMX. Autodetects the
service MBean, using its bean name as JMX object name.
-->
<context:mbean-export/>
<!-- PetClinic's central data access object: Hibernate implementation -->
<bean id="clinic" class="org.springframework.samples.petclinic.hibernate.HibernateClinic"/>
<!-- Hibernate's JMX statistics service -->
<bean name="petclinic:type=HibernateStatistics" class="org.hibernate.jmx.StatisticsService" autowire="byName"/>
</beans>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Application context definition for PetClinic on JDBC.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
Uses Apache Commons DBCP for connection pooling. See Commons DBCP documentation
for the required JAR files. Alternatively you can use another connection pool
such as C3P0, similarly configured using Spring.
-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- Transaction manager for a single JDBC DataSource (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- Transaction manager that delegates to JTA (for a transactional JNDI DataSource) -->
<!--
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
-->
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes: Spring's
@Required and @Autowired, as well as JSR 250's @PostConstruct,
@PreDestroy and @Resource (if available) and JPA's @PersistenceContext
and @PersistenceUnit (if available).
-->
<context:annotation-config/>
<!--
Instruct Spring to retrieve and apply @AspectJ aspects which are defined
as beans in this context (such as the CallMonitoringAspect below).
-->
<aop:aspectj-autoproxy/>
<!--
Instruct Spring to perform automatic transaction management on annotated classes.
The SimpleJdbcClinic implementation declares @Transactional annotations.
"proxy-target-class" is set because of SimpleJdbcClinic's @ManagedOperation usage.
-->
<tx:annotation-driven/>
<!--
Exporter that exposes the Clinic DAO and the CallMonitoringAspect via JMX,
based on the @ManagedResource, @ManagedAttribute, and @ManagedOperation annotations.
-->
<context:mbean-export/>
<!-- PetClinic's central data access object using Spring's SimpleJdbcTemplate -->
<bean id="clinic" class="org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic"/>
<!-- Call monitoring aspect that monitors call count and call invocation time -->
<bean id="callMonitor" class="org.springframework.samples.petclinic.aspects.CallMonitoringAspect"/>
</beans>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Application context definition for PetClinic on JPA.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!--
Activates a load-time weaver for the context. Any bean within the context that
implements LoadTimeWeaverAware (such as LocalContainerEntityManagerFactoryBean)
will receive a reference to the autodetected load-time weaver.
-->
<context:load-time-weaver/>
<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--
Uses Apache Commons DBCP for connection pooling. See Commons DBCP documentation
for the required JAR files. Alternatively you can use another connection pool
such as C3P0, similarly configured using Spring.
-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- JPA EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
p:databasePlatform="${jpa.databasePlatform}" p:showSql="${jpa.showSql}"/>
<!--
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"
p:database="${jpa.database}" p:showSql="${jpa.showSql}"/>
-->
<!--
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="${jpa.database}" p:showSql="${jpa.showSql}"/>
-->
</property>
</bean>
<!-- Transaction manager for a single JPA EntityManagerFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes: Spring's
@Required and @Autowired, as well as JSR 250's @PostConstruct,
@PreDestroy and @Resource (if available) and JPA's @PersistenceContext
and @PersistenceUnit (if available).
-->
<context:annotation-config/>
<!--
Instruct Spring to perform declarative transaction management
automatically on annotated classes.
-->
<tx:annotation-driven mode="aspectj"/>
<!--
Simply defining this bean will cause requests to owner names to be saved.
This aspect is defined in petclinic.jar's META-INF/aop.xml file.
Note that we can dependency inject this bean like any other bean.
-->
<bean class="org.springframework.samples.petclinic.aspects.UsageLogAspect" p:historySize="300"/>
<!--
Post-processor to perform exception translation on @Repository classes (from native
exceptions such as JPA PersistenceExceptions to Spring's DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<!--
Will automatically be transactional due to @Transactional.
EntityManager will be auto-injected due to @PersistenceContext.
PersistenceExceptions will be auto-translated due to @Repository.
-->
<bean id="clinic" class="org.springframework.samples.petclinic.jpa.EntityManagerClinic"/>
</beans>

View File

@ -0,0 +1,18 @@
# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=${petclinic.root}/WEB-INF/petclinic.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.logger.org.springframework.samples.petclinic.aspects=DEBUG

View File

@ -0,0 +1,8 @@
welcome=Welcome
required=is required
notFound=has not been found
duplicate=is already in use
nonNumeric=must be all numeric
duplicateFormSubmission=Duplicate form submission is not allowed
typeMismatch.date=invalid date
typeMismatch.birthDate=invalid date

View File

@ -0,0 +1,8 @@
welcome=Willkommen
required=muss angegeben werden
notFound=wurde nicht gefunden
duplicate=ist bereits vergeben
nonNumeric=darf nur numerisch sein
duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt
typeMismatch.date=ungültiges Datum
typeMismatch.birthDate=ungültiges Datum

View File

@ -0,0 +1 @@
# This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file.

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-1.0"
configId="org/springframework/samples/petclinic">
<context-root>/petclinic</context-root>
<context-priority-classloader>true</context-priority-classloader>
</web-app>

View File

@ -0,0 +1,19 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<%
Exception ex = (Exception) request.getAttribute("exception");
%>
<h2>Data access failure: <%= ex.getMessage() %></h2>
<p/>
<%
ex.printStackTrace(new java.io.PrintWriter(out));
%>
<p/>
<br/>
<a href="<c:url value="/welcome.do"/>">Home</a>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,24 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Find Owners:</h2>
<form:form modelAttribute="owner">
<table>
<tr>
<th>
Last Name: <form:errors path="*" cssClass="errors"/>
<br/>
<form:input path="lastName" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<td><p class="submit"><input type="submit" value="Find Owners"/></p></td>
</tr>
</table>
</form:form>
<br/>
<a href="<c:url value="/addOwner.do"/>">Add Owner</a>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,12 @@
<table class="footer">
<tr>
<td><a href="<c:url value="/welcome.do"/>">Home</a></td>
<td align="right"><img src="<c:url value="/images/springsource-logo.png"/>"/></td>
</tr>
</table>
</div>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
PetClinic :: a Spring Framework demonstration
-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="<c:url value="/styles/petclinic.css"/>" type="text/css"/>
<title>PetClinic :: a Spring Framework demonstration</title>
</head>
<body>
<div id="main">

View File

@ -0,0 +1,4 @@
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

View File

@ -0,0 +1,98 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Owner Information</h2>
<table>
<tr>
<th>Name</th>
<td><b>${owner.firstName} ${owner.lastName}</b></td>
</tr>
<tr>
<th>Address</th>
<td>${owner.address}</td>
</tr>
<tr>
<th>City</th>
<td>${owner.city}</td>
</tr>
<tr>
<th>Telephone </th>
<td>${owner.telephone}</td>
</tr>
</table>
<table class="table-buttons">
<tr>
<td colspan="2" align="center">
<form method="GET" action="<c:url value="/editOwner.do"/>">
<input type="hidden" name="ownerId" value="${owner.id}"/>
<p class="submit"><input type="submit" value="Edit Owner"/></p>
</form>
</td>
<td>
<form method="GET" action="<c:url value="/addPet.do"/>" name="formAddPet">
<input type="hidden" name="ownerId" value="${owner.id}"/>
<p class="submit"><input type="submit" value="Add New Pet"/></p>
</form>
</td>
</tr>
</table>
<h2>Pets and Visits</h2>
<c:forEach var="pet" items="${owner.pets}">
<table width="94%">
<tr>
<td valign="top">
<table>
<tr>
<th>Name</th>
<td><b>${pet.name}</b></td>
</tr>
<tr>
<th>Birth Date</th>
<td><fmt:formatDate value="${pet.birthDate}" pattern="yyyy-MM-dd"/></td>
</tr>
<tr>
<th>Type</th>
<td>${pet.type.name}</td>
</tr>
</table>
</td>
<td valign="top">
<table>
<tr>
<thead>
<th>Visit Date</th>
<th>Description</th>
</thead>
</tr>
<c:forEach var="visit" items="${pet.visits}">
<tr>
<td><fmt:formatDate value="${visit.date}" pattern="yyyy-MM-dd"/></td>
<td>${visit.description}</td>
</tr>
</c:forEach>
</table>
</td>
</tr>
</table>
<table class="table-buttons">
<tr>
<td>
<form method="GET" action="<c:url value="/editPet.do"/>" name="formEditPet${pet.id}">
<input type="hidden" name="petId" value="${pet.id}"/>
<p class="submit"><input type="submit" value="Edit Pet"/></p>
</form>
</td>
<td>
<form method="GET" action="<c:url value="/addVisit.do"/>" name="formVisitPet${pet.id}">
<input type="hidden" name="petId" value="${pet.id}"/>
<p class="submit"><input type="submit" value="Add Visit"/></p>
</form>
</td>
</tr>
</table>
</c:forEach>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,58 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2><c:if test="${owner.new}">New </c:if>Owner:</h2>
<form:form modelAttribute="owner">
<table>
<tr>
<th>
First Name: <form:errors path="firstName" cssClass="errors"/>
<br/>
<form:input path="firstName" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
Last Name: <form:errors path="lastName" cssClass="errors"/>
<br/>
<form:input path="lastName" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
Address: <form:errors path="address" cssClass="errors"/>
<br/>
<form:input path="address" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
City: <form:errors path="city" cssClass="errors"/>
<br/>
<form:input path="city" size="30" maxlength="80"/>
</th>
</tr>
<tr>
<th>
Telephone: <form:errors path="telephone" cssClass="errors"/>
<br/>
<form:input path="telephone" size="20" maxlength="20"/>
</th>
</tr>
<tr>
<td>
<c:choose>
<c:when test="${owner.new}">
<p class="submit"><input type="submit" value="Add Owner"/></p>
</c:when>
<c:otherwise>
<p class="submit"><input type="submit" value="Update Owner"/></p>
</c:otherwise>
</c:choose>
</td>
</tr>
</table>
</form:form>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,33 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Owners:</h2>
<table>
<tr>
<thead>
<th>Name</th>
<th>Address</th>
<th>City</th>
<th>Telephone</th>
<th>Pets</th>
</thead>
</tr>
<c:forEach var="owner" items="${selections}">
<tr>
<td>
<a href="owner.do?ownerId=${owner.id}">${owner.firstName} ${owner.lastName}</a>
</td>
<td>${owner.address}</td>
<td>${owner.city}</td>
<td>${owner.telephone}</td>
<td>
<c:forEach var="pet" items="${owner.pets}">
${pet.name} &nbsp;
</c:forEach>
</td>
</tr>
</c:forEach>
</table>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,47 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2><c:if test="${pet.new}">New </c:if>Pet</h2>
<b>Owner:</b> ${pet.owner.firstName} ${pet.owner.lastName}
<br/>
<form:form modelAttribute="pet">
<table>
<tr>
<th>
Name: <form:errors path="name" cssClass="errors"/>
<br/>
<form:input path="name" size="30" maxlength="30"/>
</th>
</tr>
<tr>
<th>
Birth Date: <form:errors path="birthDate" cssClass="errors"/>
<br/>
<form:input path="birthDate" size="10" maxlength="10"/> (yyyy-mm-dd)
</th>
</tr>
<tr>
<th>
Type: <form:errors path="type" cssClass="errors"/>
<br/>
<form:select path="type" items="${types}"/>
</th>
</tr>
<tr>
<td>
<c:choose>
<c:when test="${pet.new}">
<p class="submit"><input type="submit" value="Add Pet"/></p>
</c:when>
<c:otherwise>
<p class="submit"><input type="submit" value="Update Pet"/></p>
</c:otherwise>
</c:choose>
</td>
</tr>
</table>
</form:form>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,49 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2/>Internal error</h2>
<p/>
<%
try {
// The Servlet spec guarantees this attribute will be available
Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
if (exception != null) {
if (exception instanceof ServletException) {
// It's a ServletException: we should extract the root cause
ServletException sex = (ServletException) exception;
Throwable rootCause = sex.getRootCause();
if (rootCause == null)
rootCause = sex;
out.println("** Root cause is: "+ rootCause.getMessage());
rootCause.printStackTrace(new java.io.PrintWriter(out));
}
else {
// It's not a ServletException, so we'll just show it
exception.printStackTrace(new java.io.PrintWriter(out));
}
}
else {
out.println("No error information available");
}
// Display cookies
out.println("\nCookies:\n");
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
out.println(cookies[i].getName() + "=[" + cookies[i].getValue() + "]");
}
}
} catch (Exception ex) {
ex.printStackTrace(new java.io.PrintWriter(out));
}
%>
<p/>
<br/>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,26 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2>Veterinarians:</h2>
<table>
<tr>
<thead>
<th>Name</th>
<th>Specialties</th>
</thead>
</tr>
<c:forEach var="vet" items="${vetList}">
<tr>
<td>${vet.firstName} ${vet.lastName}</td>
<td>
<c:forEach var="specialty" items="${vet.specialties}">
${specialty.name}
</c:forEach>
<c:if test="${vet.nrOfSpecialties == 0}">none</c:if>
</td>
</tr>
</c:forEach>
</table>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,70 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<h2><c:if test="${visit.new}">New </c:if>Visit:</h2>
<form:form modelAttribute="visit">
<b>Pet:</b>
<table width="333">
<tr>
<thead>
<th>Name</th>
<th>Birth Date</th>
<th>Type</th>
<th>Owner</th>
</thead>
</tr>
<tr>
<td>${visit.pet.name}</td>
<td><fmt:formatDate value="${visit.pet.birthDate}" pattern="yyyy-MM-dd"/></td>
<td>${visit.pet.type.name}</td>
<td>${visit.pet.owner.firstName} ${visit.pet.owner.lastName}</td>
</tr>
</table>
<table width="333">
<tr>
<th>
Date:
<br/><form:errors path="date" cssClass="errors"/>
</th>
<td>
<form:input path="date" size="10" maxlength="10"/> (yyyy-mm-dd)
</td>
<tr/>
<tr>
<th valign="top">
Description:
<br/><form:errors path="description" cssClass="errors"/>
</th>
<td>
<form:textarea path="description" rows="10" cols="25"/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="hidden" name="petId" value="${visit.pet.id}"/>
<p class="submit"><input type="submit" value="Add Visit"/></p>
</td>
</tr>
</table>
</form:form>
<br/>
<b>Previous Visits:</b>
<table width="333">
<tr>
<th>Date</th>
<th>Description</th>
</tr>
<c:forEach var="visit" items="${visit.pet.visits}">
<c:if test="${!visit.new}">
<tr>
<td><fmt:formatDate value="${visit.date}" pattern="yyyy-MM-dd"/></td>
<td>${visit.description}</td>
</tr>
</c:if>
</c:forEach>
</table>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,16 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>
<img src="images/pets.png" align="right" style="position:relative;right:30px;">
<h2><fmt:message key="welcome"/></h2>
<ul>
<li><a href="<c:url value="/findOwners.do"/>">Find owner</a></li>
<li><a href="<c:url value="/vets.do"/>">Display all veterinarians</a></li>
<li><a href="<c:url value="/html/petclinic.html"/>">Tutorial</a></li>
<li><a href="<c:url value="/docs/index.html"/>">Documentation</a></li>
</ul>
<p>&nbsp;</p>
<%@ include file="/WEB-INF/jsp/footer.jsp" %>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- DispatcherServlet application context for PetClinic's web tier.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--
- The controllers are autodetected POJOs labeled with the @Controller annotation.
-->
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
<!--
- The form-based controllers within this application provide @RequestMapping
- annotations at the type level for path mapping URLs and @RequestMapping
- at the method level for request type mappings (e.g., GET and POST).
- In contrast, ClinicController - which is not form-based - provides
- @RequestMapping only at the method level for path mapping URLs.
-
- DefaultAnnotationHandlerMapping is driven by these annotations and is
- enabled by default with Java 5+.
-->
<!--
- This bean processes annotated handler methods, applying PetClinic-specific PropertyEditors
- for request parameter binding. It overrides the default AnnotationMethodHandlerAdapter.
-->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer"/>
</property>
</bean>
<!--
- This bean resolves specific types of exceptions to corresponding logical
- view names for error views. The default behaviour of DispatcherServlet
- is to propagate all exceptions to the servlet container: this will happen
- here with all other types of exceptions.
-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.dao.DataAccessException">dataAccessFailure</prop>
<prop key="org.springframework.transaction.TransactionException">dataAccessFailure</prop>
</props>
</property>
</bean>
<!--
- This bean configures the 'prefix' and 'suffix' properties of
- InternalResourceViewResolver, which resolves logical view names
- returned by Controllers. For example, a logical view name of "vets"
- will be mapped to "/WEB-INF/jsp/vets.jsp".
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp"/>
<!--
- Message source for this context, loaded from localized "messages_xx" files.
- Could also reside in the root application context, as it is generic,
- but is currently just used within PetClinic's web tier.
-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="messages"/>
</beans>

View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring PetClinic</display-name>
<description>Spring PetClinic sample application</description>
<!--
Key of the system property that should specify the root directory of this
web app. Applied by WebAppRootListener or Log4jConfigListener.
-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>petclinic.root</param-value>
</context-param>
<!--
Location of the Log4J config file, for initialization and refresh checks.
Applied by Log4jConfigListener.
-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<!--
- Location of the XML file that defines the root application context.
- Applied by ContextLoaderServlet.
-
- Can be set to:
- "/WEB-INF/applicationContext-hibernate.xml" for the Hibernate implementation,
- "/WEB-INF/applicationContext-jpa.xml" for the JPA one, or
- "/WEB-INF/applicationContext-jdbc.xml" for the JDBC one.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-jdbc.xml</param-value>
<!--
<param-value>/WEB-INF/applicationContext-hibernate.xml</param-value>
<param-value>/WEB-INF/applicationContext-jpa.xml</param-value>
-->
<!--
To use the JPA variant above, you will need to enable Spring load-time
weaving in your server environment. See PetClinic's readme and/or
Spring's JPA documentation for information on how to do this.
-->
</context-param>
<!--
- Configures Log4J for this web app.
- As this context specifies a context-param "log4jConfigLocation", its file path
- is used to load the Log4J configuration, including periodic refresh checks.
-
- Would fall back to default Log4J initialization (non-refreshing) if no special
- context-params are given.
-
- Exports a "web app root key", i.e. a system property that specifies the root
- directory of this web app, for usage in log file paths.
- This web app specifies "petclinic.root" (see log4j.properties file).
-->
<!-- Leave the listener commented-out if using JBoss -->
<!--
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
-->
<!--
- Loads the root application context of this web app at startup,
- by default from "/WEB-INF/applicationContext.xml".
- Note that you need to fall back to Spring's ContextLoaderServlet for
- J2EE servers that do not follow the Servlet 2.4 initialization order.
-
- Use WebApplicationContextUtils.getWebApplicationContext(servletContext)
- to access it anywhere in the web application, outside of the framework.
-
- The root context is the parent of all servlet-specific contexts.
- This means that its beans are automatically available in these child contexts,
- both for getBean(name) calls and (external) bean references.
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
- Servlet that dispatches request to registered handlers (Controller implementations).
- Has its own application context, by default defined in "{servlet-name}-servlet.xml",
- i.e. "petclinic-servlet.xml".
-
- A web app can contain any number of such servlets.
- Note that this web app has a shared root application context, serving as parent
- of all DispatcherServlet contexts.
-->
<servlet>
<servlet-name>petclinic</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!--
- Maps the petclinic dispatcher to "*.do". All handler mappings in
- petclinic-servlet.xml will by default be applied to this subpath.
- If a mapping isn't a /* subpath, the handler mappings are considered
- relative to the web app root.
-
- NOTE: A single dispatcher can be mapped to multiple paths, like any servlet.
-->
<servlet-mapping>
<servlet-name>petclinic</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<welcome-file-list>
<!-- Redirects to "welcome.htm" for dispatcher handling -->
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<!-- Displays a stack trace -->
<location>/WEB-INF/jsp/uncaughtException.jsp</location>
</error-page>
<!--
- Reference to PetClinic database.
- Only needed if not using a local DataSource but a JNDI one instead.
-->
<!--
<resource-ref>
<res-ref-name>jdbc/petclinic</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
-->
</web-app>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,5 @@
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%-- Redirected because we can't set the welcome page to a virtual URL. --%>
<c:redirect url="/welcome.do"/>

View File

@ -0,0 +1,234 @@
/* main elements */
body,div,td {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #666;
}
body {
background-color: #fff;
background-image: url(../images/banner-graphic.png);
background-position: top center;
background-repeat: no-repeat;
text-align: center;
min-width: 600px;
margin-top: 60px;
margin-left: auto;
margin-right: auto;
}
div {
margin: 5px 25px 5px 25px;
text-align: left;
}
/* header and footer elements */
#main {
margin:0 auto;
position:relative;
top: 35px;
left:0px;
width:560px;
text-align:left;
}
.footer {
background:#fff;
border:none;
margin-top:20px;
border-top:1px solid #999999;
width:100%;
}
.footer td {color:#999999;}
.footer a:link {color: #7db223;}
/* text styles */
h1,h2,h3 {
font-family: Helvetica, sans-serif;
color: #7db223;
}
h1 {
font-size: 20px;
line-height: 26px;
}
h2 {
font-size: 18px;
line-height: 24px;
}
h3 {
font-size: 15px;
line-height: 21px;
color:#555;
}
h4 {
font-size: 14px;
line-height: 20px;
}
.errors {
color: red;
font-weight: bold;
}
a {
text-decoration: underline;
font-size: 13px;
}
a:link {
color: #7db223;
}
a:hover {
color: #456314;
}
a:active {
color: #7db223;
}
a:visited {
color: #7db223;
}
ul {
list-style: disc url(../images/bullet-arrow.png);
}
li {
padding-top: 5px;
text-align: left;
}
li ul {
list-style: square url(images/sub-bullet.gif);
}
li ul li ul {
list-style: circle none;
}
/* table elements */
table {
background: #d6e2c3;
margin: 3px 0 0 0;
border: 4px solid #d6e2c3;
border-collapse: collapse;
}
table table {
margin: -5px 0;
border: 0px solid #e0e7d3;
width: 100%;
}
table td,table th {
padding: 8px;
}
table th {
font-size: 12px;
text-align: left;
font-weight: bold;
}
table thead {
font-weight: bold;
font-style: italic;
background-color: #c2ceaf;
}
table a:link {color: #303030;}
caption {
caption-side: top;
width: auto;
text-align: left;
font-size: 12px;
color: #848f73;
padding-bottom: 4px;
}
fieldset {
background: #e0e7d3;
padding: 8px;
padding-bottom: 22px;
border: none;
width: 560px;
}
fieldset label {
width: 70px;
float: left;
margin-top: 1.7em;
margin-left: 20px;
}
fieldset textfield {
margin: 3px;
height: 20px;
background: #e0e7d3;
}
fieldset textarea {
margin: 3px;
height: 165px;
background: #e0e7d3;
}
fieldset input {
margin: 3px;
height: 20px;
background: #e0e7d3;
}
fieldset table {
width: 100%;
}
fieldset th {
padding-left: 25px;
}
.table-buttons {
background-color:#fff;
border:none;
}
.table-buttons td {
border:none;
}
.submit input {
background:url(../images/submit-bg.png) repeat-x;
border: 2px outset #d7b9c9;
color:#383838;
padding:2px 10px;
font-size:11px;
text-transform:uppercase;
font-weight:bold;
}
.updated {
background:#ecf1e5;
font-size:11px;
margin-left:2px;
border:4px solid #ecf1e5;
}
.updated td {
padding:2px 8px;
font-size:11px;
color:#888888;
}

View File

@ -0,0 +1,20 @@
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<context:annotation-config/>
<tx:annotation-driven/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
p:username="${jdbc.username}" p:password="${jdbc.password}" />
</beans>

View File

@ -0,0 +1,224 @@
package org.springframework.samples.petclinic;
import java.util.Collection;
import java.util.Date;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
/**
* <p>
* Base class for {@link Clinic} integration tests.
* </p>
* <p>
* &quot;AbstractClinicTests-context.xml&quot; declares a common
* {@link javax.sql.DataSource DataSource}. Subclasses should specify
* additional context locations which declare a
* {@link org.springframework.transaction.PlatformTransactionManager PlatformTransactionManager}
* and a concrete implementation of {@link Clinic}.
* </p>
* <p>
* This class extends {@link AbstractTransactionalJUnit4SpringContextTests},
* one of the valuable testing support classes provided by the
* <em>Spring TestContext Framework</em> found in the
* <code>org.springframework.test.context</code> package. The
* annotation-driven configuration used here represents best practice for
* integration tests with Spring. Note, however, that
* AbstractTransactionalJUnit4SpringContextTests serves only as a convenience
* for extension. For example, if you do not wish for your test classes to be
* tied to a Spring-specific class hierarchy, you may configure your tests with
* annotations such as {@link ContextConfiguration @ContextConfiguration},
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners},
* {@link org.springframework.transaction.annotation.Transactional @Transactional},
* etc.
* </p>
* <p>
* AbstractClinicTests and its subclasses benefit from the following services
* provided by the Spring TestContext Framework:
* </p>
* <ul>
* <li><strong>Spring IoC container caching</strong> which spares us
* unnecessary set up time between test execution.</li>
* <li><strong>Dependency Injection</strong> of test fixture instances,
* meaning that we don't need to perform application context lookups. See the
* use of {@link Autowired @Autowired} on the <code>clinic</code> instance
* variable, which uses autowiring <em>by type</em>. As an alternative, we
* could annotate <code>clinic</code> with
* {@link javax.annotation.Resource @Resource} to achieve dependency injection
* <em>by name</em>.
* <em>(see: {@link ContextConfiguration @ContextConfiguration},
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener DependencyInjectionTestExecutionListener})</em></li>
* <li><strong>Transaction management</strong>, meaning each test method is
* executed in its own transaction, which is automatically rolled back by
* default. Thus, even if tests insert or otherwise change database state, there
* is no need for a teardown or cleanup script.
* <em>(see: {@link org.springframework.test.context.transaction.TransactionConfiguration @TransactionConfiguration},
* {@link org.springframework.transaction.annotation.Transactional @Transactional},
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener TransactionalTestExecutionListener})</em></li>
* <li><strong>Useful inherited protected fields</strong>, such as a
* {@link org.springframework.jdbc.core.simple.SimpleJdbcTemplate SimpleJdbcTemplate}
* that can be used to verify database state after test operations or to verify
* the results of queries performed by application code. An
* {@link org.springframework.context.ApplicationContext ApplicationContext} is
* also inherited and can be used for explicit bean lookup if necessary.
* <em>(see: {@link org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests AbstractJUnit4SpringContextTests},
* {@link AbstractTransactionalJUnit4SpringContextTests})</em></li>
* </ul>
* <p>
* The Spring TestContext Framework and related unit and integration testing
* support classes are shipped in <code>spring-test.jar</code>.
* </p>
*
* @author Ken Krebs
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
*/
@ContextConfiguration
public abstract class AbstractClinicTests extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
protected Clinic clinic;
@Test
public void getVets() {
Collection<Vet> vets = this.clinic.getVets();
// Use the inherited countRowsInTable() convenience method (from
// AbstractTransactionalJUnit4SpringContextTests) to verify the results.
assertEquals("JDBC query must show the same number of vets", super.countRowsInTable("VETS"), vets.size());
Vet v1 = EntityUtils.getById(vets, Vet.class, 2);
assertEquals("Leary", v1.getLastName());
assertEquals(1, v1.getNrOfSpecialties());
assertEquals("radiology", (v1.getSpecialties().get(0)).getName());
Vet v2 = EntityUtils.getById(vets, Vet.class, 3);
assertEquals("Douglas", v2.getLastName());
assertEquals(2, v2.getNrOfSpecialties());
assertEquals("dentistry", (v2.getSpecialties().get(0)).getName());
assertEquals("surgery", (v2.getSpecialties().get(1)).getName());
}
@Test
public void getPetTypes() {
Collection<PetType> petTypes = this.clinic.getPetTypes();
assertEquals("JDBC query must show the same number of pet types", super.countRowsInTable("TYPES"),
petTypes.size());
PetType t1 = EntityUtils.getById(petTypes, PetType.class, 1);
assertEquals("cat", t1.getName());
PetType t4 = EntityUtils.getById(petTypes, PetType.class, 4);
assertEquals("snake", t4.getName());
}
@Test
public void findOwners() {
Collection<Owner> owners = this.clinic.findOwners("Davis");
assertEquals(2, owners.size());
owners = this.clinic.findOwners("Daviss");
assertEquals(0, owners.size());
}
@Test
public void loadOwner() {
Owner o1 = this.clinic.loadOwner(1);
assertTrue(o1.getLastName().startsWith("Franklin"));
Owner o10 = this.clinic.loadOwner(10);
assertEquals("Carlos", o10.getFirstName());
// XXX: Add programmatic support for ending transactions with the
// TestContext Framework.
// Check lazy loading, by ending the transaction:
// endTransaction();
// Now Owners are "disconnected" from the data store.
// We might need to touch this collection if we switched to lazy loading
// in mapping files, but this test would pick this up.
o1.getPets();
}
@Test
public void insertOwner() {
Collection<Owner> owners = this.clinic.findOwners("Schultz");
int found = owners.size();
Owner owner = new Owner();
owner.setLastName("Schultz");
this.clinic.storeOwner(owner);
// assertTrue(!owner.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
owners = this.clinic.findOwners("Schultz");
assertEquals("Verifying number of owners after inserting a new one.", found + 1, owners.size());
}
@Test
public void updateOwner() throws Exception {
Owner o1 = this.clinic.loadOwner(1);
String old = o1.getLastName();
o1.setLastName(old + "X");
this.clinic.storeOwner(o1);
o1 = this.clinic.loadOwner(1);
assertEquals(old + "X", o1.getLastName());
}
@Test
public void loadPet() {
Collection<PetType> types = this.clinic.getPetTypes();
Pet p7 = this.clinic.loadPet(7);
assertTrue(p7.getName().startsWith("Samantha"));
assertEquals(EntityUtils.getById(types, PetType.class, 1).getId(), p7.getType().getId());
assertEquals("Jean", p7.getOwner().getFirstName());
Pet p6 = this.clinic.loadPet(6);
assertEquals("George", p6.getName());
assertEquals(EntityUtils.getById(types, PetType.class, 4).getId(), p6.getType().getId());
assertEquals("Peter", p6.getOwner().getFirstName());
}
@Test
public void insertPet() {
Owner o6 = this.clinic.loadOwner(6);
int found = o6.getPets().size();
Pet pet = new Pet();
pet.setName("bowser");
Collection<PetType> types = this.clinic.getPetTypes();
pet.setType(EntityUtils.getById(types, PetType.class, 2));
pet.setBirthDate(new Date());
o6.addPet(pet);
assertEquals(found + 1, o6.getPets().size());
// both storePet and storeOwner are necessary to cover all ORM tools
this.clinic.storePet(pet);
this.clinic.storeOwner(o6);
// assertTrue(!pet.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
o6 = this.clinic.loadOwner(6);
assertEquals(found + 1, o6.getPets().size());
}
@Test
public void updatePet() throws Exception {
Pet p7 = this.clinic.loadPet(7);
String old = p7.getName();
p7.setName(old + "X");
this.clinic.storePet(p7);
p7 = this.clinic.loadPet(7);
assertEquals(old + "X", p7.getName());
}
@Test
public void insertVisit() {
Pet p7 = this.clinic.loadPet(7);
int found = p7.getVisits().size();
Visit visit = new Visit();
p7.addVisit(visit);
visit.setDescription("test");
// both storeVisit and storePet are necessary to cover all ORM tools
this.clinic.storeVisit(visit);
this.clinic.storePet(p7);
// assertTrue(!visit.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
p7 = this.clinic.loadPet(7);
assertEquals(found + 1, p7.getVisits().size());
}
}

View File

@ -0,0 +1,27 @@
package org.springframework.samples.petclinic;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
/**
* JUnit test for the {@link Owner} class.
*
* @author Ken Krebs
*/
public class OwnerTests {
@Test
public void testHasPet() {
Owner owner = new Owner();
Pet fido = new Pet();
fido.setName("Fido");
assertNull(owner.getPet("Fido"));
assertNull(owner.getPet("fido"));
owner.addPet(fido);
assertEquals(fido, owner.getPet("Fido"));
assertEquals(fido, owner.getPet("fido"));
}
}

View File

@ -0,0 +1,29 @@
package org.springframework.samples.petclinic;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.samples.petclinic.hibernate.HibernateClinicTests;
import org.springframework.samples.petclinic.jdbc.SimpleJdbcClinicTests;
import org.springframework.samples.petclinic.jpa.EntityManagerClinicTests;
import org.springframework.samples.petclinic.jpa.HibernateEntityManagerClinicTests;
import org.springframework.samples.petclinic.jpa.OpenJpaEntityManagerClinicTests;
/**
* JUnit 4 based test suite for all PetClinic tests.
*
* @author Sam Brannen
*/
@RunWith(Suite.class)
@SuiteClasses({
OwnerTests.class,
SimpleJdbcClinicTests.class,
HibernateClinicTests.class,
EntityManagerClinicTests.class,
HibernateEntityManagerClinicTests.class,
OpenJpaEntityManagerClinicTests.class
})
public class PetClinicSuiteTests {
}

View File

@ -0,0 +1,30 @@
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean class="org.springframework.samples.petclinic.hibernate.HibernateClinic" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
p:dataSource-ref="dataSource" p:mappingResources="petclinic.hbm.xml">
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
<property name="eventListeners">
<map>
<entry key="merge">
<bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
</entry>
</map>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
</beans>

View File

@ -0,0 +1,20 @@
package org.springframework.samples.petclinic.hibernate;
import org.springframework.samples.petclinic.AbstractClinicTests;
import org.springframework.test.context.ContextConfiguration;
/**
* <p>
* Integration tests for the {@link HibernateClinic} implementation.
* </p>
* <p>
* "HibernateClinicTests-context.xml" determines the actual beans to test.
* </p>
*
* @author Juergen Hoeller
* @author Sam Brannen
*/
@ContextConfiguration
public class HibernateClinicTests extends AbstractClinicTests {
}

View File

@ -0,0 +1,11 @@
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
<bean class="org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic" />
</beans>

View File

@ -0,0 +1,19 @@
package org.springframework.samples.petclinic.jdbc;
import org.springframework.samples.petclinic.AbstractClinicTests;
import org.springframework.test.context.ContextConfiguration;
/**
* <p>
* Integration tests for the {@link SimpleJdbcClinic} implementation.
* </p>
* <p>
* "SimpleJdbcClinicTests-context.xml" determines the actual beans to test.
* </p>
*
* @author Thomas Risberg
*/
@ContextConfiguration
public class SimpleJdbcClinicTests extends AbstractClinicTests {
}

View File

@ -0,0 +1,199 @@
package org.springframework.samples.petclinic.jpa;
import java.util.Collection;
import java.util.Date;
import javax.persistence.EntityManager;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.Pet;
import org.springframework.samples.petclinic.PetType;
import org.springframework.samples.petclinic.Vet;
import org.springframework.samples.petclinic.Visit;
import org.springframework.samples.petclinic.util.EntityUtils;
import org.springframework.test.annotation.ExpectedException;
import org.springframework.test.jpa.AbstractJpaTests;
/**
* <p>
* This class extends {@link AbstractJpaTests}, one of the valuable test
* superclasses provided in the <code>org.springframework.test</code> package.
* This represents best practice for integration tests with Spring for JPA based
* tests which require <em>shadow class loading</em>. For all other types of
* integration testing, the <em>Spring TestContext Framework</em> is
* preferred.
* </p>
* <p>
* AbstractJpaTests and its superclasses provide the following services:
* <ul>
* <li>Injects test dependencies, meaning that we don't need to perform
* application context lookups. See the setClinic() method. Injection uses
* autowiring by type.</li>
* <li>Executes each test method in its own transaction, which is automatically
* rolled back by default. This means that even if tests insert or otherwise
* change database state, there is no need for a teardown or cleanup script.</li>
* <li>Provides useful inherited protected fields, such as a
* {@link SimpleJdbcTemplate} that can be used to verify database state after
* test operations, or verify the results of queries performed by application
* code. Alternatively, you can use protected convenience methods such as
* {@link #countRowsInTable(String)}, {@link #deleteFromTables(String[])},
* etc. An ApplicationContext is also inherited, and can be used for explicit
* lookup if necessary.</li>
* </ul>
* <p>
* {@link AbstractJpaTests} and related classes are shipped in
* <code>spring-test.jar</code>.
* </p>
*
* @author Rod Johnson
* @author Sam Brannen
* @see AbstractJpaTests
*/
public abstract class AbstractJpaClinicTests extends AbstractJpaTests {
protected Clinic clinic;
/**
* This method is provided to set the Clinic instance being tested by the
* Dependency Injection injection behaviour of the superclass from the
* <code>org.springframework.test</code> package.
*
* @param clinic clinic to test
*/
public void setClinic(Clinic clinic) {
this.clinic = clinic;
}
@ExpectedException(IllegalArgumentException.class)
public void testBogusJpql() {
this.sharedEntityManager.createQuery("SELECT RUBBISH FROM RUBBISH HEAP").executeUpdate();
}
public void testApplicationManaged() {
EntityManager appManaged = this.entityManagerFactory.createEntityManager();
appManaged.joinTransaction();
}
public void testGetVets() {
Collection<Vet> vets = this.clinic.getVets();
// Use the inherited countRowsInTable() convenience method (from
// AbstractTransactionalDataSourceSpringContextTests) to verify the
// results.
assertEquals("JDBC query must show the same number of vets", super.countRowsInTable("VETS"), vets.size());
Vet v1 = EntityUtils.getById(vets, Vet.class, 2);
assertEquals("Leary", v1.getLastName());
assertEquals(1, v1.getNrOfSpecialties());
assertEquals("radiology", (v1.getSpecialties().get(0)).getName());
Vet v2 = EntityUtils.getById(vets, Vet.class, 3);
assertEquals("Douglas", v2.getLastName());
assertEquals(2, v2.getNrOfSpecialties());
assertEquals("dentistry", (v2.getSpecialties().get(0)).getName());
assertEquals("surgery", (v2.getSpecialties().get(1)).getName());
}
public void testGetPetTypes() {
Collection<PetType> petTypes = this.clinic.getPetTypes();
assertEquals("JDBC query must show the same number of pet types", super.countRowsInTable("TYPES"),
petTypes.size());
PetType t1 = EntityUtils.getById(petTypes, PetType.class, 1);
assertEquals("cat", t1.getName());
PetType t4 = EntityUtils.getById(petTypes, PetType.class, 4);
assertEquals("snake", t4.getName());
}
public void testFindOwners() {
Collection<Owner> owners = this.clinic.findOwners("Davis");
assertEquals(2, owners.size());
owners = this.clinic.findOwners("Daviss");
assertEquals(0, owners.size());
}
public void testLoadOwner() {
Owner o1 = this.clinic.loadOwner(1);
assertTrue(o1.getLastName().startsWith("Franklin"));
Owner o10 = this.clinic.loadOwner(10);
assertEquals("Carlos", o10.getFirstName());
// Check lazy loading, by ending the transaction
endTransaction();
// Now Owners are "disconnected" from the data store.
// We might need to touch this collection if we switched to lazy loading
// in mapping files, but this test would pick this up.
o1.getPets();
}
public void testInsertOwner() {
Collection<Owner> owners = this.clinic.findOwners("Schultz");
int found = owners.size();
Owner owner = new Owner();
owner.setLastName("Schultz");
this.clinic.storeOwner(owner);
// assertTrue(!owner.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
owners = this.clinic.findOwners("Schultz");
assertEquals(found + 1, owners.size());
}
public void testUpdateOwner() throws Exception {
Owner o1 = this.clinic.loadOwner(1);
String old = o1.getLastName();
o1.setLastName(old + "X");
this.clinic.storeOwner(o1);
o1 = this.clinic.loadOwner(1);
assertEquals(old + "X", o1.getLastName());
}
public void testLoadPet() {
Collection<PetType> types = this.clinic.getPetTypes();
Pet p7 = this.clinic.loadPet(7);
assertTrue(p7.getName().startsWith("Samantha"));
assertEquals(EntityUtils.getById(types, PetType.class, 1).getId(), p7.getType().getId());
assertEquals("Jean", p7.getOwner().getFirstName());
Pet p6 = this.clinic.loadPet(6);
assertEquals("George", p6.getName());
assertEquals(EntityUtils.getById(types, PetType.class, 4).getId(), p6.getType().getId());
assertEquals("Peter", p6.getOwner().getFirstName());
}
public void testInsertPet() {
Owner o6 = this.clinic.loadOwner(6);
int found = o6.getPets().size();
Pet pet = new Pet();
pet.setName("bowser");
Collection<PetType> types = this.clinic.getPetTypes();
pet.setType(EntityUtils.getById(types, PetType.class, 2));
pet.setBirthDate(new Date());
o6.addPet(pet);
assertEquals(found + 1, o6.getPets().size());
this.clinic.storeOwner(o6);
// assertTrue(!pet.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
o6 = this.clinic.loadOwner(6);
assertEquals(found + 1, o6.getPets().size());
}
public void testUpdatePet() throws Exception {
Pet p7 = this.clinic.loadPet(7);
String old = p7.getName();
p7.setName(old + "X");
this.clinic.storePet(p7);
p7 = this.clinic.loadPet(7);
assertEquals(old + "X", p7.getName());
}
public void testInsertVisit() {
Pet p7 = this.clinic.loadPet(7);
int found = p7.getVisits().size();
Visit visit = new Visit();
p7.addVisit(visit);
visit.setDescription("test");
this.clinic.storePet(p7);
// assertTrue(!visit.isNew()); -- NOT TRUE FOR TOPLINK (before commit)
p7 = this.clinic.loadPet(7);
assertEquals(found + 1, p7.getVisits().size());
}
}

View File

@ -0,0 +1,51 @@
package org.springframework.samples.petclinic.jpa;
import java.util.List;
import org.springframework.samples.petclinic.aspects.UsageLogAspect;
/**
* <p>
* Tests for the DAO variant based on the shared EntityManager approach. Uses
* TopLink Essentials (the reference implementation) for testing.
* </p>
* <p>
* Specifically tests usage of an <code>orm.xml</code> file, loaded by the
* persistence provider through the Spring-provided persistence unit root URL.
* </p>
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class EntityManagerClinicTests extends AbstractJpaClinicTests {
private UsageLogAspect usageLogAspect;
public void setUsageLogAspect(UsageLogAspect usageLogAspect) {
this.usageLogAspect = usageLogAspect;
}
@Override
protected String[] getConfigPaths() {
return new String[] {
"applicationContext-jpaCommon.xml",
"applicationContext-toplinkAdapter.xml",
"applicationContext-entityManager.xml"
};
}
public void testUsageLogAspectIsInvoked() {
String name1 = "Schuurman";
String name2 = "Greenwood";
String name3 = "Leau";
assertTrue(this.clinic.findOwners(name1).isEmpty());
assertTrue(this.clinic.findOwners(name2).isEmpty());
List<String> namesRequested = this.usageLogAspect.getNamesRequested();
assertTrue(namesRequested.contains(name1));
assertTrue(namesRequested.contains(name2));
assertFalse(namesRequested.contains(name3));
}
}

View File

@ -0,0 +1,26 @@
package org.springframework.samples.petclinic.jpa;
/**
* <p>
* Tests for the DAO variant based on the shared EntityManager approach, using
* Hibernate EntityManager for testing instead of the reference implementation.
* </p>
* <p>
* Specifically tests usage of an <code>orm.xml</code> file, loaded by the
* persistence provider through the Spring-provided persistence unit root URL.
* </p>
*
* @author Juergen Hoeller
*/
public class HibernateEntityManagerClinicTests extends EntityManagerClinicTests {
@Override
protected String[] getConfigPaths() {
return new String[] {
"applicationContext-jpaCommon.xml",
"applicationContext-hibernateAdapter.xml",
"applicationContext-entityManager.xml"
};
}
}

View File

@ -0,0 +1,27 @@
package org.springframework.samples.petclinic.jpa;
/**
* <p>
* Tests for the DAO variant based on the shared EntityManager approach, using
* Apache OpenJPA for testing instead of the reference implementation.
* </p>
* <p>
* Specifically tests usage of an <code>orm.xml</code> file, loaded by the
* persistence provider through the Spring-provided persistence unit root URL.
* </p>
*
* @author Juergen Hoeller
*/
public class OpenJpaEntityManagerClinicTests extends EntityManagerClinicTests {
@Override
protected String[] getConfigPaths() {
return new String[] {
"applicationContext-jpaCommon.xml",
"applicationContext-openJpaAdapter.xml",
"applicationContext-entityManager.xml"
};
}
}

View File

@ -0,0 +1,16 @@
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:aspectj-autoproxy />
<bean class="org.springframework.samples.petclinic.aspects.UsageLogAspect" p:historySize="300" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="clinic" class="org.springframework.samples.petclinic.jpa.EntityManagerClinic" />
</beans>

View File

@ -0,0 +1,9 @@
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="${jpa.database}" p:showSql="${jpa.showSql}" />
</beans>

View File

@ -0,0 +1,29 @@
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<tx:annotation-driven />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}" />
<!-- Note: the specific "jpaAdapter" bean sits in adapter context file -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
</beans>

View File

@ -0,0 +1,9 @@
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter" p:database="${jpa.database}"
p:showSql="${jpa.showSql}" />
</beans>

View File

@ -0,0 +1,9 @@
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
p:databasePlatform="${jpa.databasePlatform}" p:showSql="${jpa.showSql}" />
</beans>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<logger name="org.springframework.beans">
<level value="warn" />
</logger>
<logger name="org.springframework.binding">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>