Extreme Code Makeover: PetClinic Application
Introduction
This PetClinic application is built based on the original PetClinic sample application from SpringSource. The requirements of this application are documented in the tutorial that is bundled with the original Spring's PetClinic sample. We implemented all requirements listed in that document.
Besides significant reduction in the amount of code, this implementation uses eager loading extensively which improves performance as compared with Spring's original implementation.
Analysis
PetClinic is a fairly simple application with only seven models. The following chart shows all the models and relationships among them.

We can easily translate the relationships into model code.
For example, we can have the following for models vet, vet_specialty and specialty:
public class Vet extends ActiveRecord { public void registerRelations() { hasMany("vet_specialties"); hasManyThrough("specialties", "vet_specialties"); } } public class VetSpecialty extends ActiveRecord { public void registerRelations() { belongsTo("vet"); belongsTo("specialty"); } } public class Specialty extends ActiveRecord { public void registerRelations() { hasMany("vet_specialties"); } }
There is no need to specify foreign keys here as Scooter can detect the foreign key through naming convention.
Scooter uses routes to manage actions. vets, owners, pets and visits are treated as resources. And their routes are automatically generated based on their resource definitions in routes.properties file.
By default, restful style routes are created by Scooter. The following table shows routes created for resource pets:
HTTP verb | URL | controller | action | used for |
---|---|---|---|---|
GET | /pets | pets | index | display a list of all pets |
GET | /pets/add | pets | add | return an HTML form for creating a new pet |
POST | /pets | pets | create | create a new pet |
GET | /pets/1 | pets | show | display a specific pet |
GET | /pets/1/edit | pets | edit | return an HTML form for editing a pet |
PUT | /pets/1 | pets | update | update a specific pet |
DELETE | /pets/1 | pets | delete | delete a specific pet |
In this application, deleting a pet is not listed as a requirement. Therefore we remove it from the routes by using key except as follows:
resources.name.pets=\ controller:pets; except:[delete]
Implementation
We follow the following steps to implement the site. These steps are true for almost all applications built by Scooter.
- Enter scooter dir: cd scooter
- Create app: java -jar tools/create.jar petclinic mysql
- Start up web server: java -jar tools/server.jar petclinic 8080
- Browse app website: http://localhost:8080/petclinic
- Generate code, or edit code
- Refresh browser
- Repeat the above two steps until the whole app is done

Scooter's PetClinic implementation takes advantages of many capabilities of Scooter, such as code generation, eager loading, routes, etc.
For example, you can immediately see a list of vets after just the following step:
prompt>java -jar tools/generate.jar petclinic scaffold vet
Then you can see a list of vets by hitting url http://localhost:8080/petclinic/vets

Unlike the original implementation by Spring, eager loading is used as much as possible. This is achieved by using key include. For example, when displaying an owner view, the owner information and all his/her pets and their respective visits are displayed by only one database sql query.
//OwnersController public String show() { ActiveRecord owner = Owner.where("owners.id=" + p("id")).includes("pets=>visits, pets=>type").getRecord(); if (owner == null) { flash("notice", "There is no owner record with primary key as " + p("id")); } else { setViewData("owner", owner); } return null; } //sql generated by Scooter SELECT OWNERS.ID AS OWNERS_ID, OWNERS.FIRST_NAME AS OWNERS_FIRST_NAME, OWNERS.LAST_NAME AS OWNERS_LAST_NAME, OWNERS.ADDRESS AS OWNERS_ADDRESS, OWNERS.CITY AS OWNERS_CITY, OWNERS.TELEPHONE AS OWNERS_TELEPHONE, PETS.ID AS PETS_ID, PETS.NAME AS PETS_NAME, PETS.BIRTH_DATE AS PETS_BIRTH_DATE, PETS.TYPE_ID AS PETS_TYPE_ID, PETS.OWNER_ID AS PETS_OWNER_ID, VISITS.ID AS VISITS_ID, VISITS.PET_ID AS VISITS_PET_ID, VISITS.VISIT_DATE AS VISITS_VISIT_DATE, VISITS.DESCRIPTION AS VISITS_DESCRIPTION, OWNERS_PETS.ID AS OWNERS_PETS_ID, OWNERS_PETS.NAME AS OWNERS_PETS_NAME, OWNERS_PETS.BIRTH_DATE AS OWNERS_PETS_BIRTH_DATE, OWNERS_PETS.TYPE_ID AS OWNERS_PETS_TYPE_ID, OWNERS_PETS.OWNER_ID AS OWNERS_PETS_OWNER_ID, TYPES.ID AS TYPES_ID, TYPES.NAME AS TYPES_NAME FROM OWNERS LEFT OUTER JOIN PETS ON OWNERS.ID=PETS.OWNER_ID LEFT OUTER JOIN VISITS ON PETS.ID=VISITS.PET_ID LEFT OUTER JOIN PETS OWNERS_PETS ON OWNERS.ID=OWNERS_PETS.OWNER_ID LEFT OUTER JOIN TYPES ON OWNERS_PETS.TYPE_ID=TYPES.ID WHERE OWNERS.ID = 4
Of course there are tradeoffs when a query becomes too complicated. This just shows the power of include when doing eager loading.






How many lines of code we have
The amount of code of Scooter's PetClinic application is not large. Here is a summary.
prompt>java -jar tools/codestats.jar webapps/petclinic/WEB-INF/src webapps/petclinic/WEB-INF/views/owners webapps/petclinic/WEB-INF/views/pets webapps/petclinic/WEB-INF/views/vets webapps/petclinic/WEB-INF/views/visits ------------------------------------- code total files ------------------------------------- java 281 535 12 jsp 432 481 10 ------------------------------------- summary 713 1016 22
Considering that there are 17 actions implemented in this app (see routes from index 22 to index 38), it is roughly 15 lines of Java code and 25 lines of JSP code per action.
Other deployment options
Besides using embedded Jetty web server that comes with Scooter, you may also use Apache's Tomcat. You may deploy a war to other web servers too. For more details, please refer to Scooter's deployment document.