#!/usr/bin/env groovy
/**
* ====================================================================
* Author Jim LoVerde
* Copyright 2009 NVISIA, LLC
*
* 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.
* ====================================================================
*
* OK, so this isn't a full version of the "Pet Store" application. In fact,
* it's really only the domain model with a very simple servlet to render a
* few simple actions from the PetStore. But it serves it's purpose as a
* proof of concept for how you could use GORM embedded in a single Groovy
* script. And as a proof of concept, this script uses some Spring classes
* in ways that they weren't necessarily intended, so it may not be the most
* elegant solution, but it works.
*
* And as a bonus, this script registers the GSP servlet so that any GSP files
* in the directory where you run this script can be rendered. One can
* imagine grabbing the Spring ApplicationContext inside of those GSPs
* and creating a GSP based full implementation of the PetStore.
*
* Or maybe some day finding a way to pull the Grails Controller plumbing out
* so that it could run "standalone" and you could actually write controllers
* in this script...
*
* NOTE: If running behind a firewall, you need to specify the http.proxyHost
* and http.proxyPort system properties for Grape to download the
* dependencies:
* groovy -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080 gpetstore.groovy
*/
package gpetstore
@Grab(group = 'org.springframework', module = 'spring', version = '2.5.6')
@Grab(group = 'org.springframework', module = 'spring-web', version = '2.5.6')
@Grab(group = 'hsqldb', module = 'hsqldb', version = '1.8.0.7')
@Grab(group = 'org.hibernate', module = 'hibernate-core', version = '3.3.1.GA')
@Grab(group = 'org.hibernate', module = 'hibernate-annotations', version = '3.3.1.GA')
@Grab(group = 'org.grails', module = 'grails-core', version = '1.1')
@Grab(group = 'org.grails', module = 'grails-gorm', version = '1.1')
@Grab(group = 'org.grails', module = 'grails-bootstrap', version = '1.1')
@Grab(group = 'org.grails', module = 'grails-web', version = '1.1')
@Grab(group = 'javassist', module = 'javassist', version = '3.8.0.GA')
@Grab(group = 'org.mortbay.jetty', module = 'jetty-embedded', version = '6.1.0')
@Grab(group = 'commons-lang', module = 'commons-lang', version = '2.4')
class MyAppContext extends org.springframework.context.support.ClassPathXmlApplicationContext {
protected org.springframework.core.io.Resource[] getConfigResources() {
def config = """
${manualGormConfig}
"""
return (org.springframework.core.io.Resource[]) [ new org.springframework.core.io.ByteArrayResource(config.getBytes()) ]
}
// As stated in the applicationContext.xml above, none of the rest of this code would
// be needed if the gorm:sessionFactory definition could read GORM resources from the
// runtime Groovy classpath, however, for now we have to explicitly list them and
// use the getManualGormConfig method to expand them into bean definitions
def entities = [
gpetstore.Account.class,
gpetstore.Cart.class,
gpetstore.CartItem.class,
gpetstore.Category.class,
gpetstore.Inventory.class,
gpetstore.Item.class,
gpetstore.LineItem.class,
gpetstore.Orders.class,
gpetstore.OrderStatus.class,
gpetstore.Product.class,
gpetstore.Supplier.class,
]
public String getManualGormConfig() {
def manualConfig = """
"""
entities.each { entity -> manualConfig += "${entity.name}" }
manualConfig += """
"""
entities.each { entity ->
manualConfig += """
Domain
${entity.name}
"""
}
return manualConfig
}
}
@grails.persistence.Entity
class Account {
static embedded = [ 'address' ]
String username
String password
String email
String firstName
String lastName
String status
Address address
String phone
Category favouriteCategory
String languagePreference
boolean listOption
boolean bannerOption
String bannerName
}
class Address {
String street1
String street2
String city
String state
String zip
String country
}
@grails.persistence.Entity
class Cart {
static hasMany = [ items : CartItem ]
}
import org.apache.commons.lang.builder.*
@grails.persistence.Entity
class CartItem {
Item item
int quantity
boolean inStock
BigDecimal total
public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj) }
public int hashCode() { return HashCodeBuilder.reflectionHashCode(this) }
}
@grails.persistence.Entity
class Category {
static hasMany = [ products : Product ]
String name
String description
}
@grails.persistence.Entity
class Inventory {
Item item
int quantity
}
@grails.persistence.Entity
class Item {
Product product
BigDecimal listPrice
BigDecimal unitCost
Supplier supplier
String status
String attribute1
String attribute2
String attribute3
String attribute4
String attribute5
}
@grails.persistence.Entity
class LineItem {
static belongsTo = Orders
int lineNumber
Item item
int quantity
BigDecimal unitPrice
BigDecimal total
}
@grails.persistence.Entity
class Orders {
static mapping
static hasMany = [ lineItems : LineItem ]
static embedded = [ 'shipAddress', 'billAddress']
String username
Date orderDate
String courier
BigDecimal totalPrice
Address shipAddress
Address billAddress
String billToFirstName
String billToLastName
String shipToFirstName
String shipToLastName
String creditCard
String expiryDate
String cardType
String locale
String status
}
@grails.persistence.Entity
class OrderStatus {
LineItem lineItem
java.sql.Timestamp timestamp
String status
}
@grails.persistence.Entity
class Product {
static belongsTo = Category
Category category
String name
String description
}
@grails.persistence.Entity
class Supplier {
static embedded = [ 'address' ]
String name
String status
Address address
String phone
}
@Grab(group = 'org.slf4j', module = 'slf4j-api', version = '1.4.2')
@Grab(group = 'org.slf4j', module = 'slf4j-log4j12', version = '1.4.2')
def runServer(port) {
org.apache.log4j.Logger.getRootLogger().getAllAppenders().each { it.setLayout(new org.apache.log4j.PatternLayout("%r [%t] %-5p %c %x - %m%n")) }
org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.INFO)
org.apache.log4j.Logger.getLogger("org.hibernate.SQL").setLevel(org.apache.log4j.Level.DEBUG)
// Spring setup, we need to explicitly call refresh to force the inline
// XML configuration above to be loaded
def appContext = new MyAppContext()
appContext.refresh()
// GORM Demo, load some boostrap data
// Suppliers
def abc = new Supplier(name:"ABC Pets", status:"AC",
address:new Address(street1:"600 Avon Way", street2:"", city:"Los Angeles", state:"CA", zip:"94024", country:"US"),
phone:"212-947-0797").save()
def xyz = new Supplier(name:"XYZ Pets", status:"AC",
address:new Address(street1:"700 Abalone Way", street2:"", city:"San Francisco", state:"CA", zip:"94024", country:"US"),
phone:"415-947-0797").save()
// Categories
def fish = new Category(name: 'Fish', description: 'Things that swim').save()
def dogs = new Category(name: 'Dogs', description: 'Woof woof').save()
// Products
def angelfish = new Product(category:fish, name:'AngelFish', description:'Salt Water fish from Australia').save()
def goldfish = new Product(category:fish, name:'GoldFish', description:'Fresh Water fish from China').save()
def bulldog = new Product(category:dogs, name:'Bulldog', description:'Friendly dog from England').save()
def poodle = new Product(category:dogs, name:'Poodle', description:'Cute dog from France').save()
// Items
def item1 = new Item(product:angelfish, listPrice:16.50, unitCost:10.00, supplier:abc, status:'P', attribute1:'Large', attribute2:"", attribute3:"", attribute4:"", attribute5:"" ).save()
def item2 = new Item(product:goldfish, listPrice:16.50, unitCost:10.00, supplier:abc, status:'P', attribute1:'Large', attribute2:"", attribute3:"", attribute4:"", attribute5:"" ).save()
def item3 = new Item(product:bulldog, listPrice:16.50, unitCost:10.00, supplier:abc, status:'P', attribute1:'Large', attribute2:"", attribute3:"", attribute4:"", attribute5:"" ).save()
def item4 = new Item(product:poodle, listPrice:16.50, unitCost:10.00, supplier:abc, status:'P', attribute1:'Large', attribute2:"", attribute3:"", attribute4:"", attribute5:"" ).save()
// Inventory
new Inventory(item:item1, quantity:10).save()
new Inventory(item:item2, quantity:10).save()
new Inventory(item:item3, quantity:10).save()
new Inventory(item:item4, quantity:10).save()
// Jetty setup with GSP support, Groovlet support, and a custom servlet
def server = new org.mortbay.jetty.Server(port)
def context = new org.mortbay.jetty.servlet.Context(server, "/", org.mortbay.jetty.servlet.Context.SESSIONS);
context.resourceBase = "."
context.setAttribute("appContext", appContext)
context.addServlet(MyServlet, "/")
context.addServlet(groovy.servlet.TemplateServlet, "*.gsp")
//context.addServlet(groovy.servlet.GrooovyServlet, "*.groovy")
server.start()
}
import javax.servlet.*
import javax.servlet.http.*
class MyServlet extends groovy.servlet.AbstractHttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException {
doPost(req, resp)
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException {
def action = req.getRequestURI().replaceAll("/", "")
if (action == "") { action = "index" }
if (!action.endsWith(".ico")) {
this."$action"(req, resp)
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND)
}
}
protected String getHeader() {
return """
[Home] [View Cart]
"""
}
protected void index(req, resp) { index(req, resp, null) }
protected void index(req, resp, msg) {
resp.writer.print "
${header}"
if (msg) { resp.writer.print msg }
resp.writer.print "Product Categories
"
Category.findAll().each { resp.writer.print "- ${it.name}
" }
resp.writer.print "
"
resp.writer.print ""
}
protected void viewCategory(req, resp) {
def id = req.getParameter('id')
if (!id) { resp.sendRedirect("/"); return; }
Category.withTransaction {
def category = Category.get(id)
resp.writer.print "${header}"
resp.writer.print "Products in Category: ${category.name}
"
category.products.each { resp.writer.print "- ${it.name}
" }
resp.writer.print ""
}
}
protected void viewProduct(req, resp) {
def id = req.getParameter('id')
if (!id) { resp.sendRedirect("/"); return; }
Item.withTransaction {
def product = Product.get(id)
def items = Item.findByProduct(product)
resp.writer.print "${header}"
resp.writer.print "Items for Product: ${product.name}
"
items.each { resp.writer.print "- ${it.product.description} [add to cart]
" }
resp.writer.print ""
}
}
protected void addToCart(req, resp) {
def id = req.getParameter('id')
if (!id) { resp.sendRedirect("/"); return; }
Cart.withTransaction {
def cart = getCart(req)
def item = Item.get(id)
if (!cart.items.find { it.item.id == item.id }) {
cart.items.add( new CartItem(item:item, quantity:1, inStock:true, total:item.listPrice) )
}
resp.sendRedirect("/viewCart")
}
}
protected void removeFromCart(req, resp) {
def id = req.getParameter('id')
if (!id) { resp.sendRedirect("/"); return; }
Cart.withTransaction {
def cart = getCart(req)
def item = cart.items.find { it.item.id = id }
println "cart=${cart.dump()}"
cart.removeFromItems(item)
println "cart=${cart.dump()}"
resp.sendRedirect("/viewCart")
}
}
protected void viewCart(req, resp) {
Cart.withTransaction {
def cart = getCart(req)
resp.writer.print "${header}"
resp.writer.print "Cart
"
cart.items.each {
println "it=${it.dump()}"
if (!it.item.isAttached()) { it.item.attach() }
resp.writer.print "- ${it.item.product.name} - quantity=${it.quantity} [remove from cart]
"
}
resp.writer.print "
Submit Cart"
}
}
protected void submitCart(req, resp) {
Cart.withTransaction {
def cart = getCart(req)
resp.writer.print "${header}"
resp.writer.print "Cart Submitted
"
resp.writer.print ""
req.getSession().setAttribute("cart", null)
}
}
def getCart(req) {
def cart = req.getSession().getAttribute("cart")
if (!cart) {
cart = new Cart(items:[])
req.getSession().setAttribute("cart", cart)
}
return cart
}
}
// Run the jetty server
try {
def port = (args.size() > 0) ? Integer.parseInt(args[0]) : 8080
runServer(port)
} catch (Throwable t) {
t.printStackTrace()
}