diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..8d67bc7a5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space + +[*.{java,xml}] +indent_size = 4 +trim_trailing_whitespace = true diff --git a/.springBeans b/.springBeans index c4469cbf0..44f18becd 100644 --- a/.springBeans +++ b/.springBeans @@ -1,7 +1,7 @@ 1 - + @@ -12,6 +12,9 @@ src/main/resources/spring/mvc-view-config.xml src/main/resources/spring/business-config.xml + + src/main/resources/spring/tools-config.xml + diff --git a/pom.xml b/pom.xml index 4f37aaa6e..fdd16f409 100644 --- a/pom.xml +++ b/pom.xml @@ -1,458 +1,413 @@ - - 4.0.0 - org.springframework.samples - spring-petclinic - 1.0.0-SNAPSHOT + + 4.0.0 + org.springframework.samples + spring-petclinic + 1.0.0-SNAPSHOT - petclinic - war + petclinic + war - + - - 1.6 - UTF-8 - UTF-8 + + 1.7 + UTF-8 + UTF-8 - - 4.0.1.RELEASE - 1.4.3.RELEASE + + 1.1.3.RELEASE + 1.1.0.RELEASE - - 2.2 - 1.2 - 7.0.30 - 2.2.7 + + 7.0.59 - - 4.3.1.Final + + 2.2.0 - - 4.3.1.Final + + 1.3 + 1.1.1 + 3.2.0.GA - - 7.0.42 - 2.6.8 - 2.3.1 + + 5.1.36 - - 1.7.4 + + 2.3.0 + 1.10.3 + 2.0.3-1 + 1.1.1 + 1.1.0 - - 1.1.0 - 1.7.5 + 2.7 - - 1.0 + - - 4.11 - 1.3 + + + + + io.spring.platform + platform-bom + ${spring-io-platform.version} + pom + import + + + - - 1.3 - 1.1.1 - 2.3 - 3.1.0.CR10 - - - - 2.3.0 - 1.10.3 - 2.0.3-1 - 0.9.3 - - 5.1.22 - - - - - - org.jadira.usertype - usertype.core - ${jadira-usertype-core.version} - - - org.apache.tomcat - tomcat-servlet-api - ${tomcat.servlet.version} - provided + + + org.jadira.usertype + usertype.core + ${jadira-usertype-core.version} + + + org.apache.tomcat + tomcat-servlet-api + ${tomcat.version} + provided + + + javax.servlet.jsp + javax.servlet.jsp-api + provided + + + org.apache.tomcat + tomcat-jasper-el + ${tomcat.version} + provided - - javax.servlet.jsp - jsp-api - 2.1 - provided - - - org.glassfish.web - jstl-impl - 1.2 - - - javax.servlet - servlet-api - - - - - com.sun.xml.bind - jaxb-impl - ${jaxb-impl.version} - provided - - - - org.springframework.data - spring-data-jpa - ${spring-data-jpa.version} - + + javax.servlet.jsp.jstl + javax.servlet.jsp.jstl-api + + + org.apache.taglibs + taglibs-standard-jstlel + + + + com.jayway.jsonpath + json-path + test + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-data-jpa + - - org.springframework - spring-jdbc - ${spring-framework.version} - - - org.springframework - spring-aop - ${spring-framework.version} - - - org.springframework - spring-webmvc - ${spring-framework.version} - - - org.springframework - spring-tx - ${spring-framework.version} - - - - org.springframework - spring-context-support - ${spring-framework.version} - - - org.springframework - spring-orm - ${spring-framework.version} - - - org.springframework - spring-oxm - ${spring-framework.version} - - - commons-lang - commons-lang - - - - - org.springframework - spring-jms - ${spring-framework.version} - + + org.springframework.data + spring-data-jdbc-core + ${spring-data-jdbc.version} + + + org.springframework + * + + + + + + org.springframework + spring-jdbc + + + org.springframework + spring-webmvc + + + + org.springframework + spring-context-support + + + org.springframework + spring-oxm + - - - org.apache.tomcat - tomcat-jdbc - ${tomcat-jdbc.version} - runtime - + + org.apache.tomcat + tomcat-jdbc + runtime + - - - org.slf4j - slf4j-api - ${slf4j.version} - compile - - - ch.qos.logback - logback-classic - ${logback.version} - runtime - + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + runtime + - - - rome - rome - ${rome.version} - - - - - joda-time - joda-time - ${jodatime.version} - - - joda-time - joda-time-hibernate - ${jodatime-hibernate.version} - - - joda-time - joda-time-jsptags - ${jodatime-jsptags.version} - + + + joda-time + joda-time + + + joda-time + joda-time-hibernate + ${jodatime-hibernate.version} + + + joda-time + joda-time-jsptags + ${jodatime-jsptags.version} + - - - - org.hsqldb - hsqldb - ${hsqldb.version} - runtime - + + + org.hsqldb + hsqldb + runtime + - - - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.hibernate - hibernate-validator - ${hibernate-validator.version} - - - - org.hibernate - hibernate-ehcache - ${hibernate.version} - - - net.sf.ehcache - ehcache-core - ${ehcache.version} - - - commons-logging - commons-logging - - - - - - org.webjars - bootstrap - ${webjars-bootstrap.version} - - - org.webjars - jquery-ui - ${webjars-jquery-ui.version} - - - org.webjars - jquery - ${webjars-jquery.version} - - - - - org.springframework - spring-test - ${spring-framework.version} - test - - - junit - junit - ${junit.version} - test - - - - org.hamcrest - hamcrest-library - ${hamcrest.version} - test - + + - - org.aspectj - aspectjrt - ${aspectj.version} - - - org.aspectj - aspectjweaver - ${aspectj.version} - runtime - + + + org.hibernate + hibernate-entitymanager + + + org.hibernate + hibernate-validator + + + org.hibernate + hibernate-ehcache + + + net.sf.ehcache + ehcache-core + + + commons-logging + commons-logging + + + + + + org.webjars + bootstrap + ${webjars-bootstrap.version} + + + org.webjars + jquery-ui + ${webjars-jquery-ui.version} + + + org.webjars + jquery + ${webjars-jquery.version} + - - - com.github.dandelion - datatables-jsp - ${dandelion.datatables.version} - - - com.github.dandelion - datatables-export-itext - ${dandelion.datatables.version} - - - com.github.dandelion - datatables-servlet2 - ${dandelion.datatables.version} - + + + org.springframework + spring-test + test + + + junit + junit + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + + com.github.dandelion + dandelion-jsp + ${dandelion.version} + + + com.github.dandelion + datatables-jsp + ${dandelion.datatables.version} + + + com.github.dandelion + datatables-export-itext + ${dandelion.datatables.version} + + + + com.googlecode.json-simple + json-simple + + - + + + install + + + + ${project.basedir}/src/test/java + + + ${project.basedir}/src/test/resources + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + + + + true + ${java.version} + ${java.version} + true + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.13 + + + **/*Tests.java + + + + + org.apache.maven.plugins + maven-war-plugin + 2.3 + + petclinic + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.9 + + true + true + 2.0 + + **/*.* + + + + org.springframework.ide.eclipse.core.springbuilder + + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.jdt.core.javanature + org.springframework.ide.eclipse.core.springnature + org.eclipse.m2e.core.maven2Nature + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.4 + + + jar-with-dependencies + + + + + org.apache.tomcat.maven + tomcat7-maven-plugin + 2.2 + + tomcat-development-server + 9966 + /petclinic + + + + org.codehaus.mojo + cobertura-maven-plugin + ${cobertura.version} + + + + + + + clean + check + + + + + + + + - - - - - org.springframework - spring-core - ${spring-framework.version} - - - org.springframework - spring-beans - ${spring-framework.version} - - - org.springframework - spring-context - ${spring-framework.version} - - - org.springframework - spring-orm - ${spring-framework.version} - - - org.springframework - spring-aop - ${spring-framework.version} - - - org.springframework - spring-tx - ${spring-framework.version} - - - - - - - install - - - - ${project.basedir}/src/test/java - - - ${project.basedir}/src/test/resources - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.0 - - - - - true - ${java.version} - ${java.version} - true - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.13 - - - **/*Tests.java - - - - - org.apache.maven.plugins - maven-war-plugin - 2.3 - - petclinic - - - - org.apache.maven.plugins - maven-eclipse-plugin - 2.9 - - true - true - 2.0 - - **/*.* - - - - org.springframework.ide.eclipse.core.springbuilder - - - org.eclipse.m2e.core.maven2Builder - - - - org.eclipse.jdt.core.javanature - org.springframework.ide.eclipse.core.springnature - org.eclipse.m2e.core.maven2Nature - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.4 - - - jar-with-dependencies - - - - - org.apache.tomcat.maven - tomcat7-maven-plugin - 2.0 - - tomcat-development-server - 9966 - /petclinic - - - - + + + org.codehaus.mojo + cobertura-maven-plugin + ${cobertura.version} + + + html + + + + + + + demopetclinic diff --git a/readme.md b/readme.md index c44602744..229758c3a 100644 --- a/readme.md +++ b/readme.md @@ -1,22 +1,32 @@ # Spring PetClinic Sample Application -## What does it look like? --spring-petclinic has been deployed here on cloudfoundry: http://gopetclinic.cfapps.io/ - ## Understanding the Spring Petclinic application with a few diagrams See the presentation here ## Running petclinic locally ``` - git clone https://github.com/SpringSource/spring-petclinic.git + git clone https://github.com/spring-projects/spring-petclinic.git mvn tomcat7:run ``` You can then access petclinic here: http://localhost:9966/petclinic/ ## In case you find a bug/suggested improvement for Spring Petclinic -Our issue tracker is available here: https://github.com/SpringSource/spring-petclinic/issues +Our issue tracker is available here: https://github.com/spring-projects/spring-petclinic/issues + + +## Database configuration + +In its default configuration, Petclinic uses an in-memory database (HSQLDB) which +gets populated at startup with data. A similar setup is provided for MySql in case a persistent database configuration is needed. +Note that whenever the database type is changed, the data-access.properties file needs to be updated and the mysql-connector-java artifact from the pom.xml needs to be uncommented. + +You may start a MySql database with docker: + +``` +docker run -e MYSQL_ROOT_PASSWORD=petclinic -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:5.7.8 +``` ## Working with Petclinic in Eclipse/STS @@ -34,7 +44,7 @@ If m2e is not there, just follow the install process here: http://eclipse.org/m2 1) In the command line ``` -git clone https://github.com/SpringSource/spring-petclinic.git +git clone https://github.com/spring-projects/spring-petclinic.git ``` 2) Inside Eclipse ``` @@ -46,15 +56,17 @@ File -> Import -> Maven -> Existing Maven project - + - + + + + @@ -63,10 +75,6 @@ File -> Import -> Maven -> Existing Maven project - - - - + sample usage in JSP @@ -87,6 +95,7 @@ File -> Import -> Maven -> Existing Maven project ownersList.jspvetList.jspweb.xml + datatables.properties @@ -195,5 +204,12 @@ Here is a list of them:
Inside the 'Web' layerFilesJava Config
Spring MVC- Atom integrationJava Config branch - VetsAtomView.java - mvc-view-config.xml + Petclinic uses XML configuration by default. In case you'd like to use Java Config instead, there is a Java Config branch available here. Thanks to Antoine Rey for his contribution.
Inside the 'Web' layerFiles
Spring MVC - XML integration mvc-view-config.xmlSpring MVC - ContentNegotiatingViewResolver mvc-view-config.xml
Spring MVC Test FrameworkVisitsViewTest.java
JSP custom tags @@ -78,7 +86,7 @@ File -> Import -> Maven -> Existing Maven project webjars declaration inside pom.xml
Resource mapping in Spring configuration
- sample usage in JSP
+# Contributing + +The [issue tracker](https://github.com/spring-projects/spring-petclinic/issues) is the preferred channel for bug reports, features requests and submitting pull requests. + +For pull requests, editor preferences are available in the [editor config](https://github.com/spring-projects/spring-petclinic/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at . + + diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100755 index 000000000..d84ed7c2d --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,13 @@ +# Required metadata +sonar.projectKey=java-sonar-runner-simple +sonar.projectName=Simple Java project analyzed with the SonarQube Runner +sonar.projectVersion=1.0 + +# Comma-separated paths to directories with sources (required) +sonar.sources=src + +# Language +sonar.language=java + +# Encoding of the source files +sonar.sourceEncoding=UTF-8 \ No newline at end of file diff --git a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java index b76bf6f33..55b470e1e 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java @@ -15,8 +15,6 @@ */ package org.springframework.samples.petclinic.model; -import java.io.Serializable; - import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -34,15 +32,14 @@ public class BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) protected Integer id; + public Integer getId() { + return id; + } public void setId(Integer id) { this.id = id; } - public Integer getId() { - return id; - } - public boolean isNew() { return (this.id == null); } diff --git a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java index cb36a6267..d66c97ae7 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java @@ -32,15 +32,14 @@ public class NamedEntity extends BaseEntity { @Column(name = "name") private String name; + public String getName() { + return this.name; + } public void setName(String name) { this.name = name; } - public String getName() { - return this.name; - } - @Override public String toString() { return this.getName(); diff --git a/src/main/java/org/springframework/samples/petclinic/model/Owner.java b/src/main/java/org/springframework/samples/petclinic/model/Owner.java index 840a965ed..d0158d907 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Owner.java @@ -85,19 +85,19 @@ public class Owner extends Person { this.telephone = telephone; } - protected void setPetsInternal(Set pets) { - this.pets = pets; - } - protected Set getPetsInternal() { if (this.pets == null) { - this.pets = new HashSet(); + this.pets = new HashSet<>(); } return this.pets; } + protected void setPetsInternal(Set pets) { + this.pets = pets; + } + public List getPets() { - List sortedPets = new ArrayList(getPetsInternal()); + List sortedPets = new ArrayList<>(getPetsInternal()); PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true)); return Collections.unmodifiableList(sortedPets); } @@ -141,13 +141,13 @@ public class Owner extends Person { 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(); + .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(); } } diff --git a/src/main/java/org/springframework/samples/petclinic/model/Pet.java b/src/main/java/org/springframework/samples/petclinic/model/Pet.java index 4bc2b92f7..970cdcb76 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Pet.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Pet.java @@ -32,6 +32,7 @@ import javax.persistence.Table; import org.hibernate.annotations.Type; import org.joda.time.DateTime; +import org.joda.time.LocalDate; import org.springframework.beans.support.MutableSortDefinition; import org.springframework.beans.support.PropertyComparator; import org.springframework.format.annotation.DateTimeFormat; @@ -48,9 +49,9 @@ import org.springframework.format.annotation.DateTimeFormat; public class Pet extends NamedEntity { @Column(name = "birth_date") - @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime") + @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") @DateTimeFormat(pattern = "yyyy/MM/dd") - private DateTime birthDate; + private LocalDate birthDate; @ManyToOne @JoinColumn(name = "type_id") @@ -63,44 +64,43 @@ public class Pet extends NamedEntity { @OneToMany(cascade = CascadeType.ALL, mappedBy = "pet", fetch = FetchType.EAGER) private Set visits; - - public void setBirthDate(DateTime birthDate) { - this.birthDate = birthDate; - } - - public DateTime getBirthDate() { + public LocalDate getBirthDate() { return this.birthDate; } - public void setType(PetType type) { - this.type = type; + public void setBirthDate(LocalDate birthDate) { + this.birthDate = birthDate; } public PetType getType() { return this.type; } - protected void setOwner(Owner owner) { - this.owner = owner; + public void setType(PetType type) { + this.type = type; } public Owner getOwner() { return this.owner; } - protected void setVisitsInternal(Set visits) { - this.visits = visits; + protected void setOwner(Owner owner) { + this.owner = owner; } protected Set getVisitsInternal() { if (this.visits == null) { - this.visits = new HashSet(); + this.visits = new HashSet<>(); } return this.visits; } + protected void setVisitsInternal(Set visits) { + this.visits = visits; + } + public List getVisits() { - List sortedVisits = new ArrayList(getVisitsInternal()); + List sortedVisits = new ArrayList<>(getVisitsInternal()); PropertyComparator.sort(sortedVisits, new MutableSortDefinition("date", false, false)); return Collections.unmodifiableList(sortedVisits); } diff --git a/src/main/java/org/springframework/samples/petclinic/model/PetType.java b/src/main/java/org/springframework/samples/petclinic/model/PetType.java index 50cf0399a..f60a5cd27 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/PetType.java +++ b/src/main/java/org/springframework/samples/petclinic/model/PetType.java @@ -20,6 +20,7 @@ import javax.persistence.Table; /** * @author Juergen Hoeller + * Can be Cat, Dog, Hamster... */ @Entity @Table(name = "types") diff --git a/src/main/java/org/springframework/samples/petclinic/model/Vet.java b/src/main/java/org/springframework/samples/petclinic/model/Vet.java index c58bd85b2..d93e14c46 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Vet.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Vet.java @@ -46,24 +46,23 @@ public class Vet extends Person { @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"), - inverseJoinColumns = @JoinColumn(name = "specialty_id")) + inverseJoinColumns = @JoinColumn(name = "specialty_id")) private Set specialties; + protected Set getSpecialtiesInternal() { + if (this.specialties == null) { + this.specialties = new HashSet<>(); + } + return this.specialties; + } protected void setSpecialtiesInternal(Set specialties) { this.specialties = specialties; } - protected Set getSpecialtiesInternal() { - if (this.specialties == null) { - this.specialties = new HashSet(); - } - return this.specialties; - } - @XmlElement public List getSpecialties() { - List sortedSpecs = new ArrayList(getSpecialtiesInternal()); + List sortedSpecs = new ArrayList<>(getSpecialtiesInternal()); PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true)); return Collections.unmodifiableList(sortedSpecs); } diff --git a/src/main/java/org/springframework/samples/petclinic/model/Vets.java b/src/main/java/org/springframework/samples/petclinic/model/Vets.java index e8f44a7bc..a6f05c606 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Vets.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Vets.java @@ -1,4 +1,3 @@ -/* /* * Copyright 2002-2013 the original author or authors. * @@ -36,7 +35,7 @@ public class Vets { @XmlElement public List getVetList() { if (vets == null) { - vets = new ArrayList(); + vets = new ArrayList<>(); } return vets; } diff --git a/src/main/java/org/springframework/samples/petclinic/model/Visit.java b/src/main/java/org/springframework/samples/petclinic/model/Visit.java index ea03bde74..023a8dcef 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Visit.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Visit.java @@ -23,7 +23,7 @@ import javax.persistence.Table; import org.hibernate.annotations.Type; import org.hibernate.validator.constraints.NotEmpty; -import org.joda.time.DateTime; +import org.joda.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; /** @@ -39,9 +39,9 @@ public class Visit extends BaseEntity { * Holds value of property date. */ @Column(name = "visit_date") - @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime") + @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") @DateTimeFormat(pattern = "yyyy/MM/dd") - private DateTime date; + private LocalDate date; /** * Holds value of property description. @@ -62,7 +62,7 @@ public class Visit extends BaseEntity { * Creates a new instance of Visit for the current date */ public Visit() { - this.date = new DateTime(); + this.date = new LocalDate(); } @@ -71,7 +71,7 @@ public class Visit extends BaseEntity { * * @return Value of property date. */ - public DateTime getDate() { + public LocalDate getDate() { return this.date; } @@ -80,7 +80,7 @@ public class Visit extends BaseEntity { * * @param date New value of property date. */ - public void setDate(DateTime date) { + public void setDate(LocalDate date) { this.date = date; } diff --git a/src/main/java/org/springframework/samples/petclinic/model/package-info.java b/src/main/java/org/springframework/samples/petclinic/model/package-info.java index 2723dc9ab..2730958db 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/package-info.java +++ b/src/main/java/org/springframework/samples/petclinic/model/package-info.java @@ -1,8 +1,5 @@ - /** - * * The classes in this package represent PetClinic's business layer. - * */ package org.springframework.samples.petclinic.model; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/OwnerRepository.java b/src/main/java/org/springframework/samples/petclinic/repository/OwnerRepository.java index 131833706..a0869d1e3 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/OwnerRepository.java @@ -1,18 +1,3 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ /* * Copyright 2002-2013 the original author or authors. * @@ -53,7 +38,7 @@ public interface OwnerRepository { * * @param lastName Value to search for * @return a Collection of matching Owners (or an empty Collection if none - * found) + * found) */ Collection findByLastName(String lastName) throws DataAccessException; @@ -62,8 +47,7 @@ public interface OwnerRepository { * * @param id the id to search for * @return the Owner if found - * @throws org.springframework.dao.DataRetrievalFailureException - * if not found + * @throws org.springframework.dao.DataRetrievalFailureException if not found */ Owner findById(int id) throws DataAccessException; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/PetRepository.java b/src/main/java/org/springframework/samples/petclinic/repository/PetRepository.java index 693b2e5e9..1770e8555 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/PetRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/PetRepository.java @@ -45,8 +45,7 @@ public interface PetRepository { * * @param id the id to search for * @return the Pet if found - * @throws org.springframework.dao.DataRetrievalFailureException - * if not found + * @throws org.springframework.dao.DataRetrievalFailureException if not found */ Pet findById(int id) throws DataAccessException; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java index 75e55a41a..45de1aee0 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java @@ -25,9 +25,9 @@ import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.orm.ObjectRetrievalFailureException; import org.springframework.samples.petclinic.model.Owner; @@ -35,7 +35,6 @@ import org.springframework.samples.petclinic.model.Pet; import org.springframework.samples.petclinic.model.PetType; import org.springframework.samples.petclinic.model.Visit; import org.springframework.samples.petclinic.repository.OwnerRepository; -import org.springframework.samples.petclinic.repository.VisitRepository; import org.springframework.samples.petclinic.util.EntityUtils; import org.springframework.stereotype.Repository; @@ -52,23 +51,19 @@ import org.springframework.stereotype.Repository; @Repository public class JdbcOwnerRepositoryImpl implements OwnerRepository { - private VisitRepository visitRepository; - private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private SimpleJdbcInsert insertOwner; @Autowired - public JdbcOwnerRepositoryImpl(DataSource dataSource, NamedParameterJdbcTemplate namedParameterJdbcTemplate, - VisitRepository visitRepository) { + public JdbcOwnerRepositoryImpl(DataSource dataSource, NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.insertOwner = new SimpleJdbcInsert(dataSource) - .withTableName("owners") - .usingGeneratedKeyColumns("id"); + .withTableName("owners") + .usingGeneratedKeyColumns("id"); this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); - this.visitRepository = visitRepository; } @@ -79,12 +74,12 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository { */ @Override public Collection findByLastName(String lastName) throws DataAccessException { - Map params = new HashMap(); + Map params = new HashMap<>(); params.put("lastName", lastName + "%"); List owners = this.namedParameterJdbcTemplate.query( - "SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE last_name like :lastName", - params, - ParameterizedBeanPropertyRowMapper.newInstance(Owner.class) + "SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE last_name like :lastName", + params, + BeanPropertyRowMapper.newInstance(Owner.class) ); loadOwnersPetsAndVisits(owners); return owners; @@ -98,12 +93,12 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository { public Owner findById(int id) throws DataAccessException { Owner owner; try { - Map params = new HashMap(); + Map params = new HashMap<>(); params.put("id", id); owner = this.namedParameterJdbcTemplate.queryForObject( - "SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE id= :id", - params, - ParameterizedBeanPropertyRowMapper.newInstance(Owner.class) + "SELECT id, first_name, last_name, address, city, telephone FROM owners WHERE id= :id", + params, + BeanPropertyRowMapper.newInstance(Owner.class) ); } catch (EmptyResultDataAccessException ex) { throw new ObjectRetrievalFailureException(Owner.class, id); @@ -113,20 +108,17 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository { } public void loadPetsAndVisits(final Owner owner) { - Map params = new HashMap(); - params.put("id", owner.getId().intValue()); + Map params = new HashMap<>(); + params.put("id", owner.getId()); final List pets = this.namedParameterJdbcTemplate.query( - "SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE owner_id=:id", - params, - new JdbcPetRowMapper() + "SELECT pets.id, name, birth_date, type_id, owner_id, visits.id as visit_id, visit_date, description, pet_id FROM pets LEFT OUTER JOIN visits ON pets.id = pet_id WHERE owner_id=:id", + params, + new JdbcPetVisitExtractor() ); + Collection petTypes = getPetTypes(); for (JdbcPet pet : pets) { + pet.setType(EntityUtils.getById(petTypes, PetType.class, pet.getTypeId())); owner.addPet(pet); - pet.setType(EntityUtils.getById(getPetTypes(), PetType.class, pet.getTypeId())); - List visits = this.visitRepository.findByPetId(pet.getId()); - for (Visit visit : visits) { - pet.addVisit(visit); - } } } @@ -138,16 +130,16 @@ public class JdbcOwnerRepositoryImpl implements OwnerRepository { owner.setId(newKey.intValue()); } else { this.namedParameterJdbcTemplate.update( - "UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " + - "city=:city, telephone=:telephone WHERE id=:id", - parameterSource); + "UPDATE owners SET first_name=:firstName, last_name=:lastName, address=:address, " + + "city=:city, telephone=:telephone WHERE id=:id", + parameterSource); } } public Collection getPetTypes() throws DataAccessException { return this.namedParameterJdbcTemplate.query( - "SELECT id, name FROM types ORDER BY name", new HashMap(), - ParameterizedBeanPropertyRowMapper.newInstance(PetType.class)); + "SELECT id, name FROM types ORDER BY name", new HashMap(), + BeanPropertyRowMapper.newInstance(PetType.class)); } /** diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPet.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPet.java index b548629c7..4c266b931 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPet.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPet.java @@ -18,11 +18,10 @@ package org.springframework.samples.petclinic.repository.jdbc; import org.springframework.samples.petclinic.model.Pet; /** - * Subclass of Pet that carries temporary id properties which are only relevant for a JDBC implmentation of the - * ClinicService. + * Subclass of Pet that carries temporary id properties which are only relevant for a JDBC implementation of the + * PetRepository. * * @author Juergen Hoeller - * @see JdbcClinicImpl */ class JdbcPet extends Pet { @@ -30,21 +29,20 @@ class JdbcPet extends Pet { 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 void setTypeId(int typeId) { + this.typeId = typeId; } public int getOwnerId() { return this.ownerId; } + public void setOwnerId(int ownerId) { + this.ownerId = ownerId; + } + } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRepositoryImpl.java index c594ead1b..885c2bc2f 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRepositoryImpl.java @@ -24,9 +24,9 @@ import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.orm.ObjectRetrievalFailureException; import org.springframework.samples.petclinic.model.Owner; @@ -64,8 +64,8 @@ public class JdbcPetRepositoryImpl implements PetRepository { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); this.insertPet = new SimpleJdbcInsert(dataSource) - .withTableName("pets") - .usingGeneratedKeyColumns("id"); + .withTableName("pets") + .usingGeneratedKeyColumns("id"); this.ownerRepository = ownerRepository; this.visitRepository = visitRepository; @@ -73,25 +73,25 @@ public class JdbcPetRepositoryImpl implements PetRepository { @Override public List findPetTypes() throws DataAccessException { - Map params = new HashMap(); + Map params = new HashMap<>(); return this.namedParameterJdbcTemplate.query( - "SELECT id, name FROM types ORDER BY name", - params, - ParameterizedBeanPropertyRowMapper.newInstance(PetType.class)); + "SELECT id, name FROM types ORDER BY name", + params, + BeanPropertyRowMapper.newInstance(PetType.class)); } @Override public Pet findById(int id) throws DataAccessException { JdbcPet pet; try { - Map params = new HashMap(); + Map params = new HashMap<>(); params.put("id", id); pet = this.namedParameterJdbcTemplate.queryForObject( - "SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE id=:id", - params, - new JdbcPetRowMapper()); + "SELECT id, name, birth_date, type_id, owner_id FROM pets WHERE id=:id", + params, + new JdbcPetRowMapper()); } catch (EmptyResultDataAccessException ex) { - throw new ObjectRetrievalFailureException(Pet.class, new Integer(id)); + throw new ObjectRetrievalFailureException(Pet.class, id); } Owner owner = this.ownerRepository.findById(pet.getOwnerId()); owner.addPet(pet); @@ -108,13 +108,13 @@ public class JdbcPetRepositoryImpl implements PetRepository { public void save(Pet pet) throws DataAccessException { if (pet.isNew()) { Number newKey = this.insertPet.executeAndReturnKey( - createPetParameterSource(pet)); + createPetParameterSource(pet)); pet.setId(newKey.intValue()); } else { this.namedParameterJdbcTemplate.update( - "UPDATE pets SET name=:name, birth_date=:birth_date, type_id=:type_id, " + - "owner_id=:owner_id WHERE id=:id", - createPetParameterSource(pet)); + "UPDATE pets SET name=:name, birth_date=:birth_date, type_id=:type_id, " + + "owner_id=:owner_id WHERE id=:id", + createPetParameterSource(pet)); } } @@ -123,11 +123,11 @@ public class JdbcPetRepositoryImpl implements PetRepository { */ private MapSqlParameterSource createPetParameterSource(Pet pet) { return new MapSqlParameterSource() - .addValue("id", pet.getId()) - .addValue("name", pet.getName()) - .addValue("birth_date", pet.getBirthDate().toDate()) - .addValue("type_id", pet.getType().getId()) - .addValue("owner_id", pet.getOwner().getId()); + .addValue("id", pet.getId()) + .addValue("name", pet.getName()) + .addValue("birth_date", pet.getBirthDate().toDate()) + .addValue("type_id", pet.getType().getId()) + .addValue("owner_id", pet.getOwner().getId()); } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java index 7bc551e99..6420df163 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java @@ -20,21 +20,22 @@ import java.sql.SQLException; import java.util.Date; import org.joda.time.DateTime; -import org.springframework.jdbc.core.simple.ParameterizedRowMapper; +import org.joda.time.LocalDate; +import org.springframework.jdbc.core.RowMapper; /** - * {@link ParameterizedRowMapper} implementation mapping data from a {@link ResultSet} to the corresponding properties + * {@link RowMapper} implementation mapping data from a {@link ResultSet} to the corresponding properties * of the {@link JdbcPet} class. */ -class JdbcPetRowMapper implements ParameterizedRowMapper { +class JdbcPetRowMapper implements RowMapper { @Override public JdbcPet mapRow(ResultSet rs, int rownum) throws SQLException { JdbcPet pet = new JdbcPet(); - pet.setId(rs.getInt("id")); + pet.setId(rs.getInt("pets.id")); pet.setName(rs.getString("name")); Date birthDate = rs.getDate("birth_date"); - pet.setBirthDate(new DateTime(birthDate)); + pet.setBirthDate(new LocalDate(birthDate)); pet.setTypeId(rs.getInt("type_id")); pet.setOwnerId(rs.getInt("owner_id")); return pet; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java new file mode 100644 index 000000000..6a4ba62c3 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.repository.jdbc; + +import org.springframework.data.jdbc.core.OneToManyResultSetExtractor; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.samples.petclinic.model.Visit; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * {@link ResultSetExtractor} implementation by using the + * {@link OneToManyResultSetExtractor} of Spring Data Core JDBC Extensions. + */ +public class JdbcPetVisitExtractor extends + OneToManyResultSetExtractor { + + public JdbcPetVisitExtractor() { + super(new JdbcPetRowMapper(), new JdbcVisitRowMapper()); + } + + @Override + protected Integer mapPrimaryKey(ResultSet rs) throws SQLException { + return rs.getInt("pets.id"); + } + + @Override + protected Integer mapForeignKey(ResultSet rs) throws SQLException { + if (rs.getObject("visits.pet_id") == null) { + return null; + } else { + return rs.getInt("visits.pet_id"); + } + } + + @Override + protected void addChild(JdbcPet root, Visit child) { + root.addVisit(child); + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVetRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVetRepositoryImpl.java index 05f6a24b2..0231275c1 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVetRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVetRepositoryImpl.java @@ -23,9 +23,8 @@ import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper; -import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.samples.petclinic.model.Specialty; import org.springframework.samples.petclinic.model.Vet; import org.springframework.samples.petclinic.repository.VetRepository; @@ -33,8 +32,7 @@ import org.springframework.samples.petclinic.util.EntityUtils; import org.springframework.stereotype.Repository; /** - * A simple JDBC-based implementation of the {@link VetRepository} interface. Uses @Cacheable to cache the result of the - * {@link findAll} method + * A simple JDBC-based implementation of the {@link VetRepository} interface. * * @author Ken Krebs * @author Juergen Hoeller @@ -56,33 +54,31 @@ public class JdbcVetRepositoryImpl implements VetRepository { /** * Refresh the cache of Vets that the ClinicService is holding. - * - * @see org.springframework.samples.petclinic.model.service.ClinicService#findVets() */ @Override public Collection findAll() throws DataAccessException { - List vets = new ArrayList(); + List vets = new ArrayList<>(); // Retrieve the list of all vets. vets.addAll(this.jdbcTemplate.query( - "SELECT id, first_name, last_name FROM vets ORDER BY last_name,first_name", - ParameterizedBeanPropertyRowMapper.newInstance(Vet.class))); + "SELECT id, first_name, last_name FROM vets ORDER BY last_name,first_name", + BeanPropertyRowMapper.newInstance(Vet.class))); // Retrieve the list of all possible specialties. final List specialties = this.jdbcTemplate.query( - "SELECT id, name FROM specialties", - ParameterizedBeanPropertyRowMapper.newInstance(Specialty.class)); + "SELECT id, name FROM specialties", + BeanPropertyRowMapper.newInstance(Specialty.class)); // Build each vet's list of specialties. for (Vet vet : vets) { final List vetSpecialtiesIds = this.jdbcTemplate.query( - "SELECT specialty_id FROM vet_specialties WHERE vet_id=?", - new ParameterizedRowMapper() { - @Override - public Integer mapRow(ResultSet rs, int row) throws SQLException { - return Integer.valueOf(rs.getInt(1)); - } - }, - vet.getId().intValue()); + "SELECT specialty_id FROM vet_specialties WHERE vet_id=?", + new BeanPropertyRowMapper() { + @Override + public Integer mapRow(ResultSet rs, int row) throws SQLException { + return rs.getInt(1); + } + }, + vet.getId()); for (int specialtyId : vetSpecialtiesIds) { Specialty specialty = EntityUtils.getById(specialties, Specialty.class, specialtyId); vet.addSpecialty(specialty); diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java index fee5af5b4..330ca1a4c 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java @@ -15,24 +15,18 @@ */ package org.springframework.samples.petclinic.repository.jdbc; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Date; -import java.util.List; - -import javax.sql.DataSource; - -import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.samples.petclinic.model.Visit; import org.springframework.samples.petclinic.repository.VisitRepository; import org.springframework.stereotype.Repository; +import javax.sql.DataSource; +import java.util.List; + /** * A simple JDBC-based implementation of the {@link VisitRepository} interface. * @@ -56,8 +50,8 @@ public class JdbcVisitRepositoryImpl implements VisitRepository { this.jdbcTemplate = new JdbcTemplate(dataSource); this.insertVisit = new SimpleJdbcInsert(dataSource) - .withTableName("visits") - .usingGeneratedKeyColumns("id"); + .withTableName("visits") + .usingGeneratedKeyColumns("id"); } @@ -65,46 +59,30 @@ public class JdbcVisitRepositoryImpl implements VisitRepository { public void save(Visit visit) throws DataAccessException { if (visit.isNew()) { Number newKey = this.insertVisit.executeAndReturnKey( - createVisitParameterSource(visit)); + createVisitParameterSource(visit)); visit.setId(newKey.intValue()); } else { throw new UnsupportedOperationException("Visit update not supported"); } } - public void deletePet(int id) throws DataAccessException { - this.jdbcTemplate.update("DELETE FROM pets WHERE id=?", id); - } - /** * 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().toDate()) - .addValue("description", visit.getDescription()) - .addValue("pet_id", visit.getPet().getId()); + .addValue("id", visit.getId()) + .addValue("visit_date", visit.getDate().toDate()) + .addValue("description", visit.getDescription()) + .addValue("pet_id", visit.getPet().getId()); } @Override public List findByPetId(Integer petId) { - final List visits = this.jdbcTemplate.query( - "SELECT id, visit_date, description FROM visits WHERE pet_id=?", - new ParameterizedRowMapper() { - @Override - public Visit mapRow(ResultSet rs, int row) throws SQLException { - Visit visit = new Visit(); - visit.setId(rs.getInt("id")); - Date visitDate = rs.getDate("visit_date"); - visit.setDate(new DateTime(visitDate)); - visit.setDescription(rs.getString("description")); - return visit; - } - }, - petId); - return visits; + return this.jdbcTemplate.query( + "SELECT id as visit_id, visit_date, description FROM visits WHERE pet_id=?", + new JdbcVisitRowMapper(), petId); } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java new file mode 100644 index 000000000..edc4527a3 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.repository.jdbc; + + +import org.joda.time.LocalDate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.samples.petclinic.model.Visit; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; + +/** + * {@link RowMapper} implementation mapping data from a {@link ResultSet} to the corresponding properties + * of the {@link Visit} class. + */ +class JdbcVisitRowMapper implements RowMapper { + + @Override + public Visit mapRow(ResultSet rs, int row) throws SQLException { + Visit visit = new Visit(); + visit.setId(rs.getInt("visit_id")); + Date visitDate = rs.getDate("visit_date"); + visit.setDate(new LocalDate(visitDate)); + visit.setDescription(rs.getString("description")); + return visit; + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/package-info.java b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/package-info.java index edd0bf855..376da279f 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jdbc/package-info.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jdbc/package-info.java @@ -1,9 +1,6 @@ - /** - * * The classes in this package represent the JDBC implementation * of PetClinic's persistence layer. - * */ package org.springframework.samples.petclinic.repository.jdbc; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaOwnerRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaOwnerRepositoryImpl.java index c7398df5b..3972dd349 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaOwnerRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaOwnerRepositoryImpl.java @@ -43,7 +43,7 @@ public class JpaOwnerRepositoryImpl implements OwnerRepository { /** - * Important: in the current version of this method, we load Owners with all their Pets and Visits while + * Important: in the current version of this method, we load Owners with all their Pets and Visits while * we do not need Visits at all and we only need one property from the Pet objects (the 'name' property). * There are some ways to improve it such as: * - creating a Ligtweight class (example here: https://community.jboss.org/wiki/LightweightClass) @@ -70,12 +70,11 @@ public class JpaOwnerRepositoryImpl implements OwnerRepository { @Override public void save(Owner owner) { - if (owner.getId() == null) { - this.em.persist(owner); - } - else { - this.em.merge(owner); - } + if (owner.getId() == null) { + this.em.persist(owner); + } else { + this.em.merge(owner); + } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaPetRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaPetRepositoryImpl.java index 84d564da4..227140d96 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaPetRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaPetRepositoryImpl.java @@ -53,12 +53,11 @@ public class JpaPetRepositoryImpl implements PetRepository { @Override public void save(Pet pet) { - if (pet.getId() == null) { - this.em.persist(pet); - } - else { - this.em.merge(pet); - } + if (pet.getId() == null) { + this.em.persist(pet); + } else { + this.em.merge(pet); + } } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaVisitRepositoryImpl.java b/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaVisitRepositoryImpl.java index 3415def96..5d1d42a07 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaVisitRepositoryImpl.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jpa/JpaVisitRepositoryImpl.java @@ -45,12 +45,11 @@ public class JpaVisitRepositoryImpl implements VisitRepository { @Override public void save(Visit visit) { - if (visit.getId() == null) { - this.em.persist(visit); - } - else { - this.em.merge(visit); - } + if (visit.getId() == null) { + this.em.persist(visit); + } else { + this.em.merge(visit); + } } diff --git a/src/main/java/org/springframework/samples/petclinic/repository/jpa/package-info.java b/src/main/java/org/springframework/samples/petclinic/repository/jpa/package-info.java index 13c8552ed..087dc15b6 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/jpa/package-info.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/jpa/package-info.java @@ -1,9 +1,6 @@ - /** - * * The classes in this package represent the JPA implementation * of PetClinic's persistence layer. - * */ package org.springframework.samples.petclinic.repository.jpa; diff --git a/src/main/java/org/springframework/samples/petclinic/repository/springdatajpa/SpringDataOwnerRepository.java b/src/main/java/org/springframework/samples/petclinic/repository/springdatajpa/SpringDataOwnerRepository.java index ca1f709f6..24c573eb7 100644 --- a/src/main/java/org/springframework/samples/petclinic/repository/springdatajpa/SpringDataOwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/repository/springdatajpa/SpringDataOwnerRepository.java @@ -30,12 +30,12 @@ import org.springframework.samples.petclinic.repository.OwnerRepository; * @since 15.1.2013 */ public interface SpringDataOwnerRepository extends OwnerRepository, Repository { - - @Override - @Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%") - public Collection findByLastName(@Param("lastName") String lastName); - - @Override - @Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id") - public Owner findById(@Param("id") int id); + + @Override + @Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%") + public Collection findByLastName(@Param("lastName") String lastName); + + @Override + @Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id") + public Owner findById(@Param("id") int id); } diff --git a/src/main/java/org/springframework/samples/petclinic/service/ClinicService.java b/src/main/java/org/springframework/samples/petclinic/service/ClinicService.java index c4a65e61e..936582129 100644 --- a/src/main/java/org/springframework/samples/petclinic/service/ClinicService.java +++ b/src/main/java/org/springframework/samples/petclinic/service/ClinicService.java @@ -26,25 +26,25 @@ import org.springframework.samples.petclinic.model.Visit; /** - * Mostly used as a facade for all Petclinic controllers + * Mostly used as a facade so all controllers have a single point of entry * * @author Michael Isvy */ public interface ClinicService { - public Collection findPetTypes() throws DataAccessException; + Collection findPetTypes() throws DataAccessException; - public Owner findOwnerById(int id) throws DataAccessException; + Owner findOwnerById(int id) throws DataAccessException; - public Pet findPetById(int id) throws DataAccessException; + Pet findPetById(int id) throws DataAccessException; - public void savePet(Pet pet) throws DataAccessException; + void savePet(Pet pet) throws DataAccessException; - public void saveVisit(Visit visit) throws DataAccessException; + void saveVisit(Visit visit) throws DataAccessException; - public Collection findVets() throws DataAccessException; + Collection findVets() throws DataAccessException; - public void saveOwner(Owner owner) throws DataAccessException; + void saveOwner(Owner owner) throws DataAccessException; Collection findOwnerByLastName(String lastName) throws DataAccessException; diff --git a/src/main/java/org/springframework/samples/petclinic/util/CallMonitoringAspect.java b/src/main/java/org/springframework/samples/petclinic/util/CallMonitoringAspect.java index 096f441d8..fddf17dfa 100644 --- a/src/main/java/org/springframework/samples/petclinic/util/CallMonitoringAspect.java +++ b/src/main/java/org/springframework/samples/petclinic/util/CallMonitoringAspect.java @@ -26,6 +26,8 @@ import org.springframework.util.StopWatch; /** * Simple aspect that monitors call count and call invocation time. It uses JMX annotations and therefore can be * monitored using any JMX console such as the jConsole + *

+ * This is only useful if you use JPA or JDBC. Spring-data-jpa doesn't have any correctly annotated classes to join on * * @author Rob Harrop * @author Juergen Hoeller @@ -42,17 +44,16 @@ public class CallMonitoringAspect { private long accumulatedCallTime = 0; - - @ManagedAttribute - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - @ManagedAttribute public boolean isEnabled() { return enabled; } + @ManagedAttribute + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @ManagedOperation public void reset() { this.callCount = 0; @@ -66,7 +67,10 @@ public class CallMonitoringAspect { @ManagedAttribute public long getCallTime() { - return (this.callCount > 0 ? this.accumulatedCallTime / this.callCount : 0); + if (this.callCount > 0) + return this.accumulatedCallTime / this.callCount; + else + return 0; } diff --git a/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java b/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java index a18f65c39..eee390694 100644 --- a/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java +++ b/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java @@ -39,13 +39,12 @@ public abstract class EntityUtils { * @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 + * @throws ObjectRetrievalFailureException if the entity was not found */ public static T getById(Collection entities, Class entityClass, int entityId) - throws ObjectRetrievalFailureException { + throws ObjectRetrievalFailureException { for (T entity : entities) { - if (entity.getId().intValue() == entityId && entityClass.isInstance(entity)) { + if (entity.getId() == entityId && entityClass.isInstance(entity)) { return entity; } } diff --git a/src/main/java/org/springframework/samples/petclinic/web/CrashController.java b/src/main/java/org/springframework/samples/petclinic/web/CrashController.java index e413f3f3b..29c55c1c0 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/CrashController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/CrashController.java @@ -33,7 +33,7 @@ public class CrashController { @RequestMapping(value = "/oups", method = RequestMethod.GET) public String triggerException() { throw new RuntimeException("Expected: controller used to showcase what " + - "happens when an exception is thrown"); + "happens when an exception is thrown"); } diff --git a/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java index f311110f8..0ff2858d5 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java @@ -31,8 +31,6 @@ import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.PathVariable; 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.support.SessionStatus; import org.springframework.web.servlet.ModelAndView; /** @@ -42,7 +40,6 @@ import org.springframework.web.servlet.ModelAndView; * @author Michael Isvy */ @Controller -@SessionAttributes(types = Owner.class) public class OwnerController { private final ClinicService clinicService; @@ -66,12 +63,11 @@ public class OwnerController { } @RequestMapping(value = "/owners/new", method = RequestMethod.POST) - public String processCreationForm(@Valid Owner owner, BindingResult result, SessionStatus status) { + public String processCreationForm(@Valid Owner owner, BindingResult result) { if (result.hasErrors()) { return "owners/createOrUpdateOwnerForm"; } else { this.clinicService.saveOwner(owner); - status.setComplete(); return "redirect:/owners/" + owner.getId(); } } @@ -92,19 +88,18 @@ public class OwnerController { // find owners by last name Collection results = this.clinicService.findOwnerByLastName(owner.getLastName()); - if (results.size() < 1) { + if (results.isEmpty()) { // no owners found result.rejectValue("lastName", "notFound", "not found"); return "owners/findOwners"; - } - if (results.size() > 1) { - // multiple owners found - model.put("selections", results); - return "owners/ownersList"; - } else { + } else if (results.size() == 1) { // 1 owner found owner = results.iterator().next(); return "redirect:/owners/" + owner.getId(); + } else { + // multiple owners found + model.put("selections", results); + return "owners/ownersList"; } } @@ -115,13 +110,13 @@ public class OwnerController { return "owners/createOrUpdateOwnerForm"; } - @RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.PUT) - public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, SessionStatus status) { + @RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.POST) + public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId) { if (result.hasErrors()) { return "owners/createOrUpdateOwnerForm"; } else { + owner.setId(ownerId); this.clinicService.saveOwner(owner); - status.setComplete(); return "redirect:/owners/{ownerId}"; } } diff --git a/src/main/java/org/springframework/samples/petclinic/web/PetController.java b/src/main/java/org/springframework/samples/petclinic/web/PetController.java index ea8aeaaa8..a94499759 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/PetController.java @@ -15,24 +15,20 @@ */ package org.springframework.samples.petclinic.web; -import java.util.Collection; -import java.util.Map; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.samples.petclinic.model.Owner; import org.springframework.samples.petclinic.model.Pet; import org.springframework.samples.petclinic.model.PetType; import org.springframework.samples.petclinic.service.ClinicService; import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -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.support.SessionStatus; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.Collection; /** * @author Juergen Hoeller @@ -40,12 +36,11 @@ import org.springframework.web.bind.support.SessionStatus; * @author Arjen Poutsma */ @Controller -@SessionAttributes("pet") +@RequestMapping("/owners/{ownerId}") public class PetController { private final ClinicService clinicService; - @Autowired public PetController(ClinicService clinicService) { this.clinicService = clinicService; @@ -56,48 +51,60 @@ public class PetController { return this.clinicService.findPetTypes(); } - @InitBinder - public void setAllowedFields(WebDataBinder dataBinder) { + @ModelAttribute("owner") + public Owner findOwner(@PathVariable("ownerId") int ownerId) { + Owner owner = this.clinicService.findOwnerById(ownerId); + return owner; + } + + @InitBinder("owner") + public void initOwnerBinder(WebDataBinder dataBinder) { dataBinder.setDisallowedFields("id"); } - @RequestMapping(value = "/owners/{ownerId}/pets/new", method = RequestMethod.GET) - public String initCreationForm(@PathVariable("ownerId") int ownerId, Map model) { - Owner owner = this.clinicService.findOwnerById(ownerId); + @InitBinder("pet") + public void initPetBinder(WebDataBinder dataBinder) { + dataBinder.setValidator(new PetValidator()); + } + + @RequestMapping(value = "/pets/new", method = RequestMethod.GET) + public String initCreationForm(Owner owner, ModelMap model) { Pet pet = new Pet(); owner.addPet(pet); model.put("pet", pet); return "pets/createOrUpdatePetForm"; } - @RequestMapping(value = "/owners/{ownerId}/pets/new", method = RequestMethod.POST) - public String processCreationForm(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { - new PetValidator().validate(pet, result); + @RequestMapping(value = "/pets/new", method = RequestMethod.POST) + public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) { + if (StringUtils.hasLength(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null){ + result.rejectValue("name", "duplicate", "already exists"); + } if (result.hasErrors()) { + model.put("pet", pet); return "pets/createOrUpdatePetForm"; } else { + owner.addPet(pet); this.clinicService.savePet(pet); - status.setComplete(); return "redirect:/owners/{ownerId}"; } } - @RequestMapping(value = "/owners/*/pets/{petId}/edit", method = RequestMethod.GET) - public String initUpdateForm(@PathVariable("petId") int petId, Map model) { + @RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.GET) + public String initUpdateForm(@PathVariable("petId") int petId, ModelMap model) { Pet pet = this.clinicService.findPetById(petId); model.put("pet", pet); return "pets/createOrUpdatePetForm"; } - @RequestMapping(value = "/owners/{ownerId}/pets/{petId}/edit", method = {RequestMethod.PUT, RequestMethod.POST}) - public String processUpdateForm(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { - // we're not using @Valid annotation here because it is easier to define such validation rule in Java - new PetValidator().validate(pet, result); + @RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.POST) + public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) { if (result.hasErrors()) { + model.put("pet", pet); return "pets/createOrUpdatePetForm"; } else { + owner.addPet(pet); this.clinicService.savePet(pet); - status.setComplete(); return "redirect:/owners/{ownerId}"; } } diff --git a/src/main/java/org/springframework/samples/petclinic/web/PetValidator.java b/src/main/java/org/springframework/samples/petclinic/web/PetValidator.java index ad1ebdf81..d889d1af6 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/PetValidator.java +++ b/src/main/java/org/springframework/samples/petclinic/web/PetValidator.java @@ -18,33 +18,46 @@ package org.springframework.samples.petclinic.web; import org.springframework.samples.petclinic.model.Pet; import org.springframework.util.StringUtils; import org.springframework.validation.Errors; +import org.springframework.validation.Validator; /** * Validator for Pet forms. + *

+ * We're not using Bean Validation annotations here because it is easier to define such validation rule in Java. + *

* * @author Ken Krebs * @author Juergen Hoeller */ -public class PetValidator { +public class PetValidator implements Validator { - public void validate(Pet pet, Errors errors) { + @Override + public void validate(Object obj, Errors errors) { + Pet pet = (Pet) obj; String name = pet.getName(); - // name validaation + // name validation 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"); } - - // type valication + + // type validation if (pet.isNew() && pet.getType() == null) { errors.rejectValue("type", "required", "required"); } - - // type valication - if (pet.getBirthDate()==null) { + + // birth date validation + if (pet.getBirthDate() == null) { errors.rejectValue("birthDate", "required", "required"); } } + /** + * This Validator validates *just* Pet instances + */ + @Override + public boolean supports(Class clazz) { + return Pet.class.isAssignableFrom(clazz); + } + + } diff --git a/src/main/java/org/springframework/samples/petclinic/web/VetController.java b/src/main/java/org/springframework/samples/petclinic/web/VetController.java index 3a6c052ea..3121e60e8 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/VetController.java @@ -22,6 +22,7 @@ import org.springframework.samples.petclinic.model.Vets; import org.springframework.samples.petclinic.service.ClinicService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; /** * @author Juergen Hoeller @@ -40,9 +41,9 @@ public class VetController { this.clinicService = clinicService; } - @RequestMapping("/vets") + @RequestMapping(value = {"/vets.xml", "/vets.html"}) public String showVetList(Map model) { - // Here we are returning an object of type 'Vets' rather than a collection of Vet objects + // Here we are returning an object of type 'Vets' rather than a collection of Vet objects // so it is simpler for Object-Xml mapping Vets vets = new Vets(); vets.getVetList().addAll(this.clinicService.findVets()); @@ -50,5 +51,16 @@ public class VetController { return "vets/vetList"; } + @RequestMapping("/vets.json") + public + @ResponseBody + Vets showResourcesVetList() { + // Here we are returning an object of type 'Vets' rather than a collection of Vet objects + // so it is simpler for JSon/Object mapping + Vets vets = new Vets(); + vets.getVetList().addAll(this.clinicService.findVets()); + return vets; + } + } diff --git a/src/main/java/org/springframework/samples/petclinic/web/VetsAtomView.java b/src/main/java/org/springframework/samples/petclinic/web/VetsAtomView.java deleted file mode 100644 index 3de619ac4..000000000 --- a/src/main/java/org/springframework/samples/petclinic/web/VetsAtomView.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.samples.petclinic.web; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.samples.petclinic.model.Vet; -import org.springframework.samples.petclinic.model.Vets; -import org.springframework.web.servlet.view.feed.AbstractAtomFeedView; - -import com.sun.syndication.feed.atom.Content; -import com.sun.syndication.feed.atom.Entry; -import com.sun.syndication.feed.atom.Feed; - -/** - * A view creating a Atom representation from a list of Visit objects. - * - * @author Alef Arendsen - * @author Arjen Poutsma - */ -public class VetsAtomView extends AbstractAtomFeedView { - - @Override - protected void buildFeedMetadata(Map model, Feed feed, HttpServletRequest request) { - feed.setId("tag:springsource.org"); - feed.setTitle("Veterinarians"); - //feed.setUpdated(date); - } - - @Override - protected List buildFeedEntries(Map model, - HttpServletRequest request, HttpServletResponse response) throws Exception { - - Vets vets = (Vets) model.get("vets"); - List vetList = vets.getVetList(); - List entries = new ArrayList(vetList.size()); - - for (Vet vet : vetList) { - Entry entry = new Entry(); - // see http://diveintomark.org/archives/2004/05/28/howto-atom-id#other - entry.setId(String.format("tag:springsource.org,%s", vet.getId())); - entry.setTitle(String.format("Vet: %s %s", vet.getFirstName(), vet.getLastName())); - //entry.setUpdated(visit.getDate().toDate()); - - Content summary = new Content(); - summary.setValue(vet.getSpecialties().toString()); - entry.setSummary(summary); - - entries.add(entry); - } - response.setContentType("blabla"); - return entries; - - } - -} diff --git a/src/main/java/org/springframework/samples/petclinic/web/VisitController.java b/src/main/java/org/springframework/samples/petclinic/web/VisitController.java index 4f38bd574..144eba2d9 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/web/VisitController.java @@ -27,12 +27,10 @@ import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; 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.support.SessionStatus; -import org.springframework.web.servlet.ModelAndView; /** * @author Juergen Hoeller @@ -41,7 +39,6 @@ import org.springframework.web.servlet.ModelAndView; * @author Michael Isvy */ @Controller -@SessionAttributes("visit") public class VisitController { private final ClinicService clinicService; @@ -57,31 +54,45 @@ public class VisitController { dataBinder.setDisallowedFields("id"); } - @RequestMapping(value = "/owners/*/pets/{petId}/visits/new", method = RequestMethod.GET) - public String initNewVisitForm(@PathVariable("petId") int petId, Map model) { + /** + * Called before each and every @RequestMapping annotated method. + * 2 goals: + * - Make sure we always have fresh data + * - Since we do not use the session scope, make sure that Pet object always has an id + * (Even though id is not part of the form fields) + * + * @param petId + * @return Pet + */ + @ModelAttribute("visit") + public Visit loadPetWithVisit(@PathVariable("petId") int petId) { Pet pet = this.clinicService.findPetById(petId); Visit visit = new Visit(); pet.addVisit(visit); - model.put("visit", visit); + return visit; + } + + // Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is called + @RequestMapping(value = "/owners/*/pets/{petId}/visits/new", method = RequestMethod.GET) + public String initNewVisitForm(@PathVariable("petId") int petId, Map model) { return "pets/createOrUpdateVisitForm"; } + // Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is called @RequestMapping(value = "/owners/{ownerId}/pets/{petId}/visits/new", method = RequestMethod.POST) - public String processNewVisitForm(@Valid Visit visit, BindingResult result, SessionStatus status) { + public String processNewVisitForm(@Valid Visit visit, BindingResult result) { if (result.hasErrors()) { return "pets/createOrUpdateVisitForm"; } else { this.clinicService.saveVisit(visit); - status.setComplete(); return "redirect:/owners/{ownerId}"; } } @RequestMapping(value = "/owners/*/pets/{petId}/visits", method = RequestMethod.GET) - public ModelAndView showVisits(@PathVariable int petId) { - ModelAndView mav = new ModelAndView("visitList"); - mav.addObject("visits", this.clinicService.findPetById(petId).getVisits()); - return mav; + public String showVisits(@PathVariable int petId, Map model) { + model.put("visits", this.clinicService.findPetById(petId).getVisits()); + return "visitList"; } } diff --git a/src/main/java/org/springframework/samples/petclinic/web/package-info.java b/src/main/java/org/springframework/samples/petclinic/web/package-info.java index c909ccf7f..ca189f19a 100644 --- a/src/main/java/org/springframework/samples/petclinic/web/package-info.java +++ b/src/main/java/org/springframework/samples/petclinic/web/package-info.java @@ -1,8 +1,5 @@ - /** - * * The classes in this package represent PetClinic's web presentation layer. - * */ package org.springframework.samples.petclinic.web; diff --git a/src/main/java/test.html b/src/main/java/test.html new file mode 100644 index 000000000..8c5cb2cf3 --- /dev/null +++ b/src/main/java/test.html @@ -0,0 +1,53 @@ + + + + +

Organisation

+ +

Speakers

+ + + + + + + + + +
+ Sergiu Bodiu + +

Sergiu Bodiu

+ +

Java Consultant at Bank of America

+ Seasoned consultant experienced in large-scale e-commerce projects, passionate about + providing innovative technology solutions to solve complex business problems, have extensive knowledge and + experience delivering enterprise wide applications. He is skilled in software design, data modeling, + stakeholder management, IT strategic planning, technical know-how and security. Able to design, implement, + test and maintain software product components with strong focus on design elegance and software reuse. +
+ Sergiu Bodiu + +

Sergiu Bodiu

+ +

Java Consultant at Bank of America

+ Seasoned consultant experienced in large-scale e-commerce projects, passionate about + providing innovative technology solutions to solve complex business problems, have extensive knowledge and + experience delivering enterprise wide applications. He is skilled in software design, data modeling, + stakeholder management, IT strategic planning, technical know-how and security. Able to design, implement, + test and maintain software product components with strong focus on design elegance and software reuse. +
diff --git a/src/main/resources/dandelion/datatables/datatables.properties b/src/main/resources/dandelion/datatables/datatables.properties new file mode 100644 index 000000000..08b1e439c --- /dev/null +++ b/src/main/resources/dandelion/datatables/datatables.properties @@ -0,0 +1,6 @@ +# ================================== +# Dandelion-Datatables configuration +# ================================== + +# Disable the asset management of Dandelion-Core for all non-DataTable-related assets +main.standalone=true \ No newline at end of file diff --git a/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt b/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt index 66a7c2f20..765711ea9 100644 --- a/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt +++ b/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt @@ -19,6 +19,6 @@ 3) Create the PetClinic database and user by executing the "db/mysql/createDB.txt" script. -4) Open "src/main/resources/spring/jdbc.properties"; comment out all properties in the +4) Open "src/main/resources/spring/data-access.properties"; comment out all properties in the "HSQL Settings" section; uncomment all properties in the "MySQL Settings" - section. \ No newline at end of file + section. diff --git a/src/main/resources/db_readme.txt b/src/main/resources/db_readme.txt deleted file mode 100644 index 55fbb6264..000000000 --- a/src/main/resources/db_readme.txt +++ /dev/null @@ -1,13 +0,0 @@ -================================================================================ -=== Spring PetClinic sample application - Database Configuration === -================================================================================ - -@author Costin Leau - --------------------------------------------------------------------------------- - -In its default configuration, Petclinic uses an in-memory database (HSQLDB) which -gets populated at startup with data. A similar setup is provided for Mysql in case -a persistent database configuration is needed. -Note that whenever the database type is changed, the jdbc.properties file needs to -be updated. \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 751726a14..ba2b51463 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,4 +1,6 @@ + + @@ -14,9 +16,9 @@ - + - + diff --git a/src/main/resources/spring/business-config.xml b/src/main/resources/spring/business-config.xml index 99cf4c1d3..a029c45ab 100644 --- a/src/main/resources/spring/business-config.xml +++ b/src/main/resources/spring/business-config.xml @@ -2,36 +2,36 @@ - - + + base-package="org.springframework.samples.petclinic.service"/> - - - - - - + + + + + + @@ -93,4 +93,4 @@ - \ No newline at end of file + diff --git a/src/main/resources/spring/data-access.properties b/src/main/resources/spring/data-access.properties index c1cc3cefd..e154d81db 100644 --- a/src/main/resources/spring/data-access.properties +++ b/src/main/resources/spring/data-access.properties @@ -4,6 +4,12 @@ # various application context XML files (e.g., "applicationContext-*.xml"). # Targeted at system administrators, to avoid touching the context XML files. +# Properties that control the population of schema and data for a new data source +jdbc.initLocation=classpath:db/hsqldb/initDB.sql +jdbc.dataLocation=classpath:db/hsqldb/populateDB.sql + +jpa.showSql=true + #------------------------------------------------------------------------------- # HSQL Settings @@ -12,14 +18,9 @@ jdbc.url=jdbc:hsqldb:mem:petclinic jdbc.username=sa jdbc.password= -# Properties that control the population of schema and data for a new data source -jdbc.initLocation=classpath:db/hsqldb/initDB.sql -jdbc.dataLocation=classpath:db/hsqldb/populateDB.sql - # Property that determines which database to use with an AbstractJpaVendorAdapter jpa.database=HSQL -jpa.showSql=true #------------------------------------------------------------------------------- # MySQL Settings @@ -27,15 +28,7 @@ jpa.showSql=true #jdbc.driverClassName=com.mysql.jdbc.Driver #jdbc.url=jdbc:mysql://localhost:3306/petclinic #jdbc.username=root -#jdbc.password= - -# Properties that control the population of schema and data for a new data source -#jdbc.initLocation=classpath:db/mysql/initDB.sql -#jdbc.dataLocation=classpath:db/mysql/populateDB.sql - -# Property that determines which Hibernate dialect to use -# (only applied with "applicationContext-hibernate.xml") -#hibernate.dialect=org.hibernate.dialect.MySQLDialect +#jdbc.password=petclinic # Property that determines which database to use with an AbstractJpaVendorAdapter #jpa.database=MYSQL diff --git a/src/main/resources/spring/datasource-config.xml b/src/main/resources/spring/datasource-config.xml index f74129963..aa705de14 100644 --- a/src/main/resources/spring/datasource-config.xml +++ b/src/main/resources/spring/datasource-config.xml @@ -2,11 +2,11 @@ - - + - \ No newline at end of file + diff --git a/src/main/resources/spring/mvc-core-config.xml b/src/main/resources/spring/mvc-core-config.xml index 170fbdba1..641ea4408 100644 --- a/src/main/resources/spring/mvc-core-config.xml +++ b/src/main/resources/spring/mvc-core-config.xml @@ -2,11 +2,11 @@ - + base-package="org.springframework.samples.petclinic.web"/> @@ -31,9 +31,10 @@ - + - + @@ -52,7 +53,7 @@ p:basename="messages/messages"/> diff --git a/src/main/resources/spring/mvc-view-config.xml b/src/main/resources/spring/mvc-view-config.xml index 4413f0195..9ba6723c5 100644 --- a/src/main/resources/spring/mvc-view-config.xml +++ b/src/main/resources/spring/mvc-view-config.xml @@ -2,54 +2,30 @@ - + - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - + + + + diff --git a/src/main/resources/spring/tools-config.xml b/src/main/resources/spring/tools-config.xml index 1be7e3b6c..61db5e57d 100644 --- a/src/main/resources/spring/tools-config.xml +++ b/src/main/resources/spring/tools-config.xml @@ -2,11 +2,11 @@ - <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - +
diff --git a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp index 76c184417..3cb66aa25 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/bodyHeader.jsp @@ -7,17 +7,15 @@ diff --git a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp index dccec75d2..c9992c782 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp @@ -2,9 +2,9 @@ - - + + diff --git a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp b/src/main/webapp/WEB-INF/jsp/fragments/staticFiles.jsp similarity index 83% rename from src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp rename to src/main/webapp/WEB-INF/jsp/fragments/staticFiles.jsp index f1eea21e6..add28329f 100644 --- a/src/main/webapp/WEB-INF/jsp/fragments/headTag.jsp +++ b/src/main/webapp/WEB-INF/jsp/fragments/staticFiles.jsp @@ -18,13 +18,13 @@ PetClinic :: a Spring Framework demonstration - + - + - + diff --git a/src/main/webapp/WEB-INF/jsp/owners/createOrUpdateOwnerForm.jsp b/src/main/webapp/WEB-INF/jsp/owners/createOrUpdateOwnerForm.jsp index 18d45cffd..30329e107 100644 --- a/src/main/webapp/WEB-INF/jsp/owners/createOrUpdateOwnerForm.jsp +++ b/src/main/webapp/WEB-INF/jsp/owners/createOrUpdateOwnerForm.jsp @@ -1,5 +1,6 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> @@ -10,20 +11,16 @@ - +
- - - -

New Owner

- + diff --git a/src/main/webapp/WEB-INF/jsp/owners/findOwners.jsp b/src/main/webapp/WEB-INF/jsp/owners/findOwners.jsp index a41d07956..f6b1930eb 100644 --- a/src/main/webapp/WEB-INF/jsp/owners/findOwners.jsp +++ b/src/main/webapp/WEB-INF/jsp/owners/findOwners.jsp @@ -1,5 +1,6 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -7,7 +8,7 @@ - +
diff --git a/src/main/webapp/WEB-INF/jsp/owners/ownerDetails.jsp b/src/main/webapp/WEB-INF/jsp/owners/ownerDetails.jsp index 0cb449dd1..8e7e10bbd 100644 --- a/src/main/webapp/WEB-INF/jsp/owners/ownerDetails.jsp +++ b/src/main/webapp/WEB-INF/jsp/owners/ownerDetails.jsp @@ -1,5 +1,6 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> @@ -8,7 +9,7 @@ - +
@@ -33,17 +34,17 @@ Telephone - - - + + + Edit Owner - + - Add New Pet + Add New Pet @@ -77,21 +78,21 @@ - - - - - - Edit Pet - - - - - - Add Visit + + + + + Edit Pet - + + + + + + Add Visit + + diff --git a/src/main/webapp/WEB-INF/jsp/owners/ownersList.jsp b/src/main/webapp/WEB-INF/jsp/owners/ownersList.jsp index 6165782c0..2e88f0fb8 100644 --- a/src/main/webapp/WEB-INF/jsp/owners/ownersList.jsp +++ b/src/main/webapp/WEB-INF/jsp/owners/ownersList.jsp @@ -1,5 +1,6 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> @@ -8,15 +9,15 @@ - +

Owners

- - + + @@ -34,9 +35,9 @@ - + - +
diff --git a/src/main/webapp/WEB-INF/jsp/pets/createOrUpdatePetForm.jsp b/src/main/webapp/WEB-INF/jsp/pets/createOrUpdatePetForm.jsp index 6a740609c..985984aba 100644 --- a/src/main/webapp/WEB-INF/jsp/pets/createOrUpdatePetForm.jsp +++ b/src/main/webapp/WEB-INF/jsp/pets/createOrUpdatePetForm.jsp @@ -1,5 +1,6 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> @@ -8,32 +9,25 @@ - +
- - - - - - - -

New Pet

- +
diff --git a/src/main/webapp/WEB-INF/jsp/pets/createOrUpdateVisitForm.jsp b/src/main/webapp/WEB-INF/jsp/pets/createOrUpdateVisitForm.jsp index 4c65be890..a90e757f8 100644 --- a/src/main/webapp/WEB-INF/jsp/pets/createOrUpdateVisitForm.jsp +++ b/src/main/webapp/WEB-INF/jsp/pets/createOrUpdateVisitForm.jsp @@ -1,20 +1,23 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> +<%@ taglib prefix="petclinic" tagdir="/WEB-INF/tags" %> + - +
@@ -40,22 +43,10 @@ -
- -
- - -
-
-
- + + -
- - -
-
diff --git a/src/main/webapp/WEB-INF/jsp/vets/vetList.jsp b/src/main/webapp/WEB-INF/jsp/vets/vetList.jsp index 07bbf636a..1c57ea93c 100644 --- a/src/main/webapp/WEB-INF/jsp/vets/vetList.jsp +++ b/src/main/webapp/WEB-INF/jsp/vets/vetList.jsp @@ -1,5 +1,6 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> @@ -8,7 +9,7 @@ - +
@@ -16,7 +17,8 @@

Veterinarians

- + @@ -27,14 +29,14 @@ none - +
">View as XML - ">Subscribe to Atom feed + ">View as JSon
diff --git a/src/main/webapp/WEB-INF/jsp/welcome.jsp b/src/main/webapp/WEB-INF/jsp/welcome.jsp index 3e18b4274..a1cf5fcae 100644 --- a/src/main/webapp/WEB-INF/jsp/welcome.jsp +++ b/src/main/webapp/WEB-INF/jsp/welcome.jsp @@ -1,12 +1,13 @@ - + +<%@ page session="false" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - +
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index d9a3ac47d..ea965de25 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -2,30 +2,32 @@ + http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" + version="3.0" metadata-complete="true"> Spring PetClinic Spring PetClinic sample application - + + spring.profiles.active jpa - - + + - - + + + + - datatablesController - com.github.dandelion.datatables.extras.servlet2.servlet.DatatablesServlet + dandelionServlet + com.github.dandelion.core.web.DandelionServlet + 2 - - - datatablesController - /datatablesController/* + dandelionServlet + /dandelion-assets/* - + - httpMethodFilter - org.springframework.web.filter.HiddenHttpMethodFilter + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + - httpMethodFilter - petclinic - - - - - encodingFilter - org.springframework.web.filter.CharacterEncodingFilter - - encoding - UTF-8 - - - forceEncoding - true - - - - - encodingFilter - /* - - - - - datatablesFilter - com.github.dandelion.datatables.extras.servlet2.filter.DatatablesFilter - - - - - datatablesFilter + encodingFilter /* - + + + + dandelionFilter + com.github.dandelion.core.web.DandelionFilter + + + dandelionFilter + /* + + + + + datatables + com.github.dandelion.datatables.core.web.filter.DatatablesFilter + + + datatables + /* + + - \ No newline at end of file + diff --git a/src/main/webapp/resources/css/petclinic.css b/src/main/webapp/resources/css/petclinic.css index 36ad67082..76a8ef83d 100644 --- a/src/main/webapp/resources/css/petclinic.css +++ b/src/main/webapp/resources/css/petclinic.css @@ -1,6 +1,5 @@ .container { padding-top: 10px; - margin-left: 50px; width: 700px; } diff --git a/src/main/webapp/resources/images/spring-pivotal-logo.png b/src/main/webapp/resources/images/spring-pivotal-logo.png new file mode 100644 index 000000000..1840af274 Binary files /dev/null and b/src/main/webapp/resources/images/spring-pivotal-logo.png differ diff --git a/src/test/java/org/springframework/samples/petclinic/model/OwnerTests.java b/src/test/java/org/springframework/samples/petclinic/model/OwnerTests.java deleted file mode 100644 index d5044fd78..000000000 --- a/src/test/java/org/springframework/samples/petclinic/model/OwnerTests.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.samples.petclinic.model; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import org.junit.Test; -import org.springframework.transaction.annotation.Transactional; - -/** - * JUnit test for the {@link Owner} class. - * - * @author Ken Krebs - */ -public class OwnerTests { - - @Test - @Transactional - 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")); - } - -} diff --git a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java index 354c15a9f..b836d0cc2 100644 --- a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java +++ b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java @@ -1,37 +1,34 @@ package org.springframework.samples.petclinic.model; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Locale; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validator; - -import org.junit.Assert; import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; /** - * * @author Michael Isvy - * Simple test to make sure that Bean Validation is working - * (useful when upgrading to a new version of Hibernate Validator/ Bean Validation) - * + * Simple test to make sure that Bean Validation is working + * (useful when upgrading to a new version of Hibernate Validator/ Bean Validation) */ public class ValidatorTests { - - private Validator createValidator() { - LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); - localValidatorFactoryBean.afterPropertiesSet(); - return localValidatorFactoryBean; - } - @Test - public void emptyFirstName() { + private Validator createValidator() { + LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); + localValidatorFactoryBean.afterPropertiesSet(); + return localValidatorFactoryBean; + } + @Test + public void shouldNotValidateWhenFirstNameEmpty() { + + LocaleContextHolder.setLocale(Locale.ENGLISH); Person person = new Person(); person.setFirstName(""); person.setLastName("smith"); @@ -39,10 +36,10 @@ public class ValidatorTests { Validator validator = createValidator(); Set> constraintViolations = validator.validate(person); - Assert.assertEquals(1, constraintViolations.size()); - ConstraintViolation violation = constraintViolations.iterator().next(); - Assert.assertEquals(violation.getPropertyPath().toString(), "firstName"); - Assert.assertEquals(violation.getMessage(), "may not be empty"); + assertThat(constraintViolations.size()).isEqualTo(1); + ConstraintViolation violation = constraintViolations.iterator().next(); + assertThat(violation.getPropertyPath().toString()).isEqualTo("firstName"); + assertThat(violation.getMessage()).isEqualTo("may not be empty"); } - + } diff --git a/src/test/java/org/springframework/samples/petclinic/service/AbstractClinicServiceTests.java b/src/test/java/org/springframework/samples/petclinic/service/AbstractClinicServiceTests.java index 02a21ac4d..469c24b47 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/AbstractClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/AbstractClinicServiceTests.java @@ -15,14 +15,12 @@ */ package org.springframework.samples.petclinic.service; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Collection; import org.joda.time.DateTime; -import org.junit.Assert; +import org.joda.time.LocalDate; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.samples.petclinic.model.Owner; @@ -41,7 +39,7 @@ import org.springframework.transaction.annotation.Transactional; * TestContext Framework:

  • Spring IoC container caching which spares us unnecessary set up * time between test execution.
  • Dependency Injection of test fixture instances, meaning that * we don't need to perform application context lookups. See the use of {@link Autowired @Autowired} on the {@link - * AbstractclinicServiceTests#clinicService clinicService} instance variable, which uses autowiring by + * AbstractClinicServiceTests#clinicService clinicService} instance variable, which uses autowiring by * type.
  • Transaction management, 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.
  • An {@link org.springframework.context.ApplicationContext @@ -59,29 +57,29 @@ public abstract class AbstractClinicServiceTests { protected ClinicService clinicService; @Test - @Transactional - public void findOwners() { + public void shouldFindOwnersByLastName() { Collection owners = this.clinicService.findOwnerByLastName("Davis"); - assertEquals(2, owners.size()); + assertThat(owners.size()).isEqualTo(2); + owners = this.clinicService.findOwnerByLastName("Daviss"); - assertEquals(0, owners.size()); + assertThat(owners.isEmpty()); } @Test - public void findSingleOwner() { - Owner owner1 = this.clinicService.findOwnerById(1); - assertTrue(owner1.getLastName().startsWith("Franklin")); - Owner owner10 = this.clinicService.findOwnerById(10); - assertEquals("Carlos", owner10.getFirstName()); - - assertEquals(owner1.getPets().size(), 1); + public void shouldFindSingleOwnerWithPet() { + Owner owner = this.clinicService.findOwnerById(1); + assertThat(owner.getLastName()).startsWith("Franklin"); + assertThat(owner.getPets().size()).isEqualTo(1); + assertThat(owner.getPets().get(0).getType()).isNotNull(); + assertThat(owner.getPets().get(0).getType().getName()).isEqualTo("cat"); } @Test @Transactional - public void insertOwner() { + public void shouldInsertOwner() { Collection owners = this.clinicService.findOwnerByLastName("Schultz"); int found = owners.size(); + Owner owner = new Owner(); owner.setFirstName("Sam"); owner.setLastName("Schultz"); @@ -89,106 +87,108 @@ public abstract class AbstractClinicServiceTests { owner.setCity("Wollongong"); owner.setTelephone("4444444444"); this.clinicService.saveOwner(owner); - Assert.assertNotEquals("Owner Id should have been generated", owner.getId().longValue(), 0); + assertThat(owner.getId().longValue()).isNotEqualTo(0); + owners = this.clinicService.findOwnerByLastName("Schultz"); - assertEquals("Verifying number of owners after inserting a new one.", found + 1, owners.size()); + assertThat(owners.size()).isEqualTo(found + 1); } @Test @Transactional - public void updateOwner() throws Exception { - Owner o1 = this.clinicService.findOwnerById(1); - String old = o1.getLastName(); - o1.setLastName(old + "X"); - this.clinicService.saveOwner(o1); - o1 = this.clinicService.findOwnerById(1); - assertEquals(old + "X", o1.getLastName()); + public void shouldUpdateOwner() { + Owner owner = this.clinicService.findOwnerById(1); + String oldLastName = owner.getLastName(); + String newLastName = oldLastName + "X"; + + owner.setLastName(newLastName); + this.clinicService.saveOwner(owner); + + // retrieving new name from database + owner = this.clinicService.findOwnerById(1); + assertThat(owner.getLastName()).isEqualTo(newLastName); } - @Test - public void findPet() { - Collection types = this.clinicService.findPetTypes(); - Pet pet7 = this.clinicService.findPetById(7); - assertTrue(pet7.getName().startsWith("Samantha")); - assertEquals(EntityUtils.getById(types, PetType.class, 1).getId(), pet7.getType().getId()); - assertEquals("Jean", pet7.getOwner().getFirstName()); - Pet pet6 = this.clinicService.findPetById(6); - assertEquals("George", pet6.getName()); - assertEquals(EntityUtils.getById(types, PetType.class, 4).getId(), pet6.getType().getId()); - assertEquals("Peter", pet6.getOwner().getFirstName()); - } + @Test + public void shouldFindPetWithCorrectId() { + Pet pet7 = this.clinicService.findPetById(7); + assertThat(pet7.getName()).startsWith("Samantha"); + assertThat(pet7.getOwner().getFirstName()).isEqualTo("Jean"); - @Test - public void getPetTypes() { - Collection petTypes = this.clinicService.findPetTypes(); - - PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1); - assertEquals("cat", petType1.getName()); - PetType petType4 = EntityUtils.getById(petTypes, PetType.class, 4); - assertEquals("snake", petType4.getName()); - } + } - @Test - @Transactional - public void insertPet() { - Owner owner6 = this.clinicService.findOwnerById(6); - int found = owner6.getPets().size(); - Pet pet = new Pet(); - pet.setName("bowser"); - Collection types = this.clinicService.findPetTypes(); - pet.setType(EntityUtils.getById(types, PetType.class, 2)); - pet.setBirthDate(new DateTime()); - owner6.addPet(pet); - assertEquals(found + 1, owner6.getPets().size()); - // both storePet and storeOwner are necessary to cover all ORM tools - this.clinicService.savePet(pet); - this.clinicService.saveOwner(owner6); - owner6 = this.clinicService.findOwnerById(6); - assertEquals(found + 1, owner6.getPets().size()); - assertNotNull("Pet Id should have been generated", pet.getId()); - } + @Test + public void shouldFindAllPetTypes() { + Collection petTypes = this.clinicService.findPetTypes(); - @Test - @Transactional - public void updatePet() throws Exception { - Pet pet7 = this.clinicService.findPetById(7); - String old = pet7.getName(); - pet7.setName(old + "X"); - this.clinicService.savePet(pet7); - pet7 = this.clinicService.findPetById(7); - assertEquals(old + "X", pet7.getName()); - } + PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1); + assertThat(petType1.getName()).isEqualTo("cat"); + PetType petType4 = EntityUtils.getById(petTypes, PetType.class, 4); + assertThat(petType4.getName()).isEqualTo("snake"); + } - @Test - public void findVets() { - Collection vets = this.clinicService.findVets(); - - 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 + @Transactional + public void shouldInsertPetIntoDatabaseAndGenerateId() { + Owner owner6 = this.clinicService.findOwnerById(6); + int found = owner6.getPets().size(); - @Test - @Transactional - public void insertVisit() { - Pet pet7 = this.clinicService.findPetById(7); - int found = pet7.getVisits().size(); - Visit visit = new Visit(); - pet7.addVisit(visit); - visit.setDescription("test"); - // both storeVisit and storePet are necessary to cover all ORM tools - this.clinicService.saveVisit(visit); - this.clinicService.savePet(pet7); - pet7 = this.clinicService.findPetById(7); - assertEquals(found + 1, pet7.getVisits().size()); - assertNotNull("Visit Id should have been generated", visit.getId()); - } + Pet pet = new Pet(); + pet.setName("bowser"); + Collection types = this.clinicService.findPetTypes(); + pet.setType(EntityUtils.getById(types, PetType.class, 2)); + pet.setBirthDate(new LocalDate()); + owner6.addPet(pet); + assertThat(owner6.getPets().size()).isEqualTo(found + 1); + + this.clinicService.savePet(pet); + this.clinicService.saveOwner(owner6); + + owner6 = this.clinicService.findOwnerById(6); + assertThat(owner6.getPets().size()).isEqualTo(found + 1); + // checks that id has been generated + assertThat(pet.getId()).isNotNull(); + } + + @Test + @Transactional + public void shouldUpdatePetName() throws Exception { + Pet pet7 = this.clinicService.findPetById(7); + String oldName = pet7.getName(); + + String newName = oldName + "X"; + pet7.setName(newName); + this.clinicService.savePet(pet7); + + pet7 = this.clinicService.findPetById(7); + assertThat(pet7.getName()).isEqualTo(newName); + } + + @Test + public void shouldFindVets() { + Collection vets = this.clinicService.findVets(); + + Vet vet = EntityUtils.getById(vets, Vet.class, 3); + assertThat(vet.getLastName()).isEqualTo("Douglas"); + assertThat(vet.getNrOfSpecialties()).isEqualTo(2); + assertThat(vet.getSpecialties().get(0).getName()).isEqualTo("dentistry"); + assertThat(vet.getSpecialties().get(1).getName()).isEqualTo("surgery"); + } + + @Test + @Transactional + public void shouldAddNewVisitForPet() { + Pet pet7 = this.clinicService.findPetById(7); + int found = pet7.getVisits().size(); + Visit visit = new Visit(); + pet7.addVisit(visit); + visit.setDescription("test"); + this.clinicService.saveVisit(visit); + this.clinicService.savePet(pet7); + + pet7 = this.clinicService.findPetById(7); + assertThat(pet7.getVisits().size()).isEqualTo(found + 1); + assertThat(visit.getId()).isNotNull(); + } } diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJdbcTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJdbcTests.java index 49e57ea40..74c5df7cf 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJdbcTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJdbcTests.java @@ -21,11 +21,11 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** - *

    Integration test using the jdbc profile. - * @see AbstractClinicServiceTests AbstractClinicServiceTests for more details.

    + *

    Integration test using the jdbc profile. * * @author Thomas Risberg * @author Michael Isvy + * @see AbstractClinicServiceTests AbstractClinicServiceTests for more details.

    */ @ContextConfiguration(locations = {"classpath:spring/business-config.xml"}) @RunWith(SpringJUnit4ClassRunner.class) diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java index e024f21fd..dba2b195a 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java @@ -1,4 +1,3 @@ - package org.springframework.samples.petclinic.service; import org.junit.runner.RunWith; @@ -7,12 +6,12 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** - *

    Integration test using the jpa profile. - * @see AbstractClinicServiceTests AbstractClinicServiceTests for more details.

    + *

    Integration test using the jpa profile. * * @author Rod Johnson * @author Sam Brannen * @author Michael Isvy + * @see AbstractClinicServiceTests AbstractClinicServiceTests for more details.

    */ @ContextConfiguration(locations = {"classpath:spring/business-config.xml"}) @@ -20,4 +19,4 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ActiveProfiles("jpa") public class ClinicServiceJpaTests extends AbstractClinicServiceTests { -} \ No newline at end of file +} diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceSpringDataJpaTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceSpringDataJpaTests.java index e01dda551..31b62289f 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceSpringDataJpaTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceSpringDataJpaTests.java @@ -1,4 +1,3 @@ - package org.springframework.samples.petclinic.service; import org.junit.runner.RunWith; @@ -7,9 +6,10 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** - *

    Integration test using the 'Spring Data' profile. - * @see AbstractClinicServiceTests AbstractClinicServiceTests for more details.

    + *

    Integration test using the 'Spring Data' profile. + * * @author Michael Isvy + * @see AbstractClinicServiceTests AbstractClinicServiceTests for more details.

    */ @ContextConfiguration(locations = {"classpath:spring/business-config.xml"}) @@ -17,4 +17,4 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ActiveProfiles("spring-data-jpa") public class ClinicServiceSpringDataJpaTests extends AbstractClinicServiceTests { -} \ No newline at end of file +} diff --git a/src/test/java/org/springframework/samples/petclinic/web/VetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/web/VetControllerTests.java new file mode 100644 index 000000000..28b9348a1 --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/web/VetControllerTests.java @@ -0,0 +1,48 @@ +package org.springframework.samples.petclinic.web; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +/** + * Test class for the UserResource REST controller. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration({"classpath:spring/business-config.xml", "classpath:spring/tools-config.xml", "classpath:spring/mvc-core-config.xml"}) +@WebAppConfiguration +@ActiveProfiles("spring-data-jpa") +public class VetControllerTests { + + @Autowired + private VetController vetController; + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.standaloneSetup(vetController).build(); + } + + @Test + public void testGetExistingUser() throws Exception { + ResultActions actions = mockMvc.perform(get("/vets.json").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + actions.andExpect(content().contentType("application/json;charset=UTF-8")) + .andExpect(jsonPath("$.vetList[0].id").value(1)); + } +} diff --git a/src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests-config.xml b/src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests-config.xml deleted file mode 100644 index 8458ba2ec..000000000 --- a/src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests-config.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests.java b/src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests.java deleted file mode 100644 index ace89a019..000000000 --- a/src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.samples.petclinic.web; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - - -/** - * @author Arjen Poutsma - * @author Michael Isvy - */ -@RunWith(SpringJUnit4ClassRunner.class) -@WebAppConfiguration -@ContextConfiguration("VisitsViewTests-config.xml") -@ActiveProfiles("jdbc") -public class VisitsViewTests { - - @Autowired - private WebApplicationContext webApplicationContext; - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build(); - } - - @Test - public void getVisitsXml() throws Exception { - ResultActions actions = this.mockMvc.perform(get("/vets.xml").accept(MediaType.APPLICATION_XML)); - actions.andDo(print()); // action is logged into the console - actions.andExpect(status().isOk()); - actions.andExpect(content().contentType("application/xml")); - actions.andExpect(xpath("/vets/vetList[id=1]/firstName").string(containsString("James"))); - - } -} diff --git a/src/test/jmeter/petclinic_test_plan.jmx b/src/test/jmeter/petclinic_test_plan.jmx new file mode 100644 index 000000000..cd36ba08c --- /dev/null +++ b/src/test/jmeter/petclinic_test_plan.jmx @@ -0,0 +1,411 @@ + + + + + + false + false + + + + PETCLINIC_HOST + localhost + = + + + PETCLINIC_PORT + 8080 + = + + + CONTEXT_WEB + + = + + + + + + + + continue + + false + 10 + + 500 + 10 + 1361531541000 + 1361531541000 + false + + + true + Original : 500 - 10 - 10 + + + + 300 + + + + + + + ${PETCLINIC_HOST} + ${PETCLINIC_PORT} + + + + + + 4 + + + + + true + + + + 1 + 10 + 1 + count + + false + + + + 1 + 13 + 1 + petCount + + false + + + + + + + + + + + + + ${CONTEXT_WEB}/ + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/resources/css/petclinic.css + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/webjars/jquery/2.0.3/jquery.js + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/vets.html + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/owners/find.html + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/owners.html?lastName= + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/owners/${count}.html + GET + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/owners/${count}/edit.html + GET + true + false + true + false + false + + + + + true + + + + false + firstName=Test&lastName=${count}&address=1234+Test+St.&city=TestCity&telephone=612345678 + = + + + + + + + + + + ${CONTEXT_WEB}/owners/${count}/edit.html + POST + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new + GET + true + false + true + false + false + + + + + true + + + + false + date=2013%2F02%2F22&description=visit + = + + + + + + + + + + ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new + POST + true + false + true + false + false + + + + + + + + + + + + + + ${CONTEXT_WEB}/owners/${count}.html + GET + true + false + true + false + false + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + false + false + false + false + false + 0 + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + false + false + false + false + false + 0 + true + true + + + + + + + + +