SlideShare uma empresa Scribd logo
1 de 62
Baixar para ler offline
Griffon in Front
 Grails in Back
 Leveraging Grails with Griffon
Griffon in Front
 Grails in Back
 Leveraging Grails with Griffon
Abstract
Groovy and Grails have given us the ability to leverage the
strength of the Java Platform (and Eco System) and the
productivity of “Convention over Configuration” to construct
websites. But “What If” the User Interface requirements of the
new application is best solved with the type of interaction a
desktop application provides?

Griffon bring the same productivity gains to the desktop
application that Grails brings to web applications. This session
will use Griffon and popular open source libraries to build a
desktop applicaiton to interact with a Grails backend.
Introduction
My name is Jim Shingler
Chief Technical Architect
President of Genuine Solutions
Beginning Groovy and Grails
Co-Author
FallME (Inversion of Control for JavaME)
Co-Founder
Griffon Founders
Griffon Founders

   Danno Ferrin
   http://shemnon.com/speling

   Andres Almiray
   http://jroller.com/aalmiray

   James Williams
   http://jameswilliams.be/blog
Common Complaints
   about Swing
Its hard
Have to code too much
Code is complex
Not many Advanced GUI Components (Myth)
Start Small
• Swing and SwingX Builder
• GUI Components
• About Box
• Define and Process Actions
Builders
      The Builder Pattern is a software design pattern. The
      intention is to separate the construction of a
      complex object from its representation so that the
      same construction process can create different
      representations.

      Often, the Build Patter is used to build Products in
      accordance to the Composite pattern.
                         Swing is a complex hierarchy, . . .
Source: Wikipedia

                           Sounds like an opportunity
GUI Builders
•   SwingBuilder
    Applies the Builder Pattern to the construction of Swing
    “Makes Swing Groovy”

•   SwingXBuilder
    Extends SwingBuilder adding the SwingLabs Components

•   JideBuilder
    Extends SwingBuilder adding JIDE Components

•   SWTBuilder
    Applies the Builder Pattern to the construction of SWT
    “Makes SWT Groovy”
Plugins
     A plugin is a Griffon extension point. Conceptually, it
     is similar to the plugins found in modern IDEs.
     A plugin is a technique to encapsulate functionality
     that can be reused across multiple applications.
     Griffon’s plugin community has just begun but it is
     growing fast.
     See: >griffon list-plugins


http://grails.org/Plugins
http://www.grails.org/The+Plug-in+Developers+Guide
DEMO
• Create Count App
• Add Button
• Build and Initialize “Click Action”
• Process the Click Action
• Install and Enable SwingXBuilder
• Build and Initialize Menus
• Build and Initialize “Menu Actions”
• Process the Menu Actions
DEMO
• Create Count App
• Add Button
• Build and Initialize “Click Action”
• Process the Click Action
• Install and Enable SwingXBuilder
• Build and Initialize Menus
• Build and Initialize “Menu Actions”
• Process the Menu Actions         NOTE:
                                   The Code included on the
                                   next several pages has
                                   been enhanced based
                                   upon questions asked in
                                   the session.
Controller
import javax.swing.JOptionPane

class Sample2Controller {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {
        // this method is called after model and view are injected
    }

    def click = { evt = null ->
    model.count++
    }

    def exit = { evt = null ->
        System.exit(0)
    }

    def showAbout = { evt = null ->
    JOptionPane.showMessageDialog(null,
        '''This is the SimpleUI Application''')
    }
}
Model
import groovy.beans.Bindable

@Bindable
class Sample2Model {
    def count = 0
}
View
application(title:'sample2', /*size:[320,480], */location:[200,200],
pack:true, locationByPlatform:false) {
    // add content here
    build(Actions)
    build(MenuBar)
    button(id:'clickButton', text:bind{ model.count }, action: clickAction)
}




                        MenuBar
                   jxmenuBar {
                       menu(text: 'File', mnemonic: 'F') {
                           menuItem(exitAction)
                       }

                       glue()
                       menu(text: 'Help', mnemonic: 'H') {
                           menuItem(aboutAction)
                       }
                   }
Actions
// create the actions
action(id: 'clickAction',
         name: 'Click Me',
         closure: controller.&click,
         shortDescription: 'Increment the Click Count'
         )

action(id: 'exitAction',
         name: 'Exit', closure: controller.exit,
         mnemonic: 'x', accelerator: 'F4',
         shortDescription: 'Exit SimpleUI'
         )

action(id: 'aboutAction',
         name: 'About', closure: controller.showAbout,
         mnemonic: 'A', accelerator: 'F1',
         shortDescription: 'Find out about SimpleUI'
         )
Goal
Goal
 Menu Bar
  Tool Bar




Content Area




 Status Bar
Goal
 Menu Bar
  Tool Bar
Login Dialog


Content Area




 Tips Dialog




 Status Bar
Planning
•   Menu Bar

•   Tool Bar

•   Status Bar

•   About Box

•   Tips

•   Login Dialog

•   Master / Detail Content Area
    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box

•   Tips

•   Login Dialog

•   Master / Detail Content Area
    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box

•   Tips

•   Login Dialog

•   Master / Detail Content Area
    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box      
•   Tips

•   Login Dialog

•   Master / Detail Content Area
    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box      
•   Tips

•   Login Dialog

•   Master / Detail Content Area
    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box      
•   Tips

•   Login Dialog

•   Master / Detail Content Area    Glazed Lists

    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box      
•   Tips

•   Login Dialog

•   Master / Detail Content Area    Glazed Lists

    •Table and Fields

•   Wire it together with Actions

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box      
•   Tips

•   Login Dialog

•   Master / Detail Content Area   Glazed Lists

    •Table and Fields

•   Wire it together with Actions 

•   Make calls to Web Services
Planning
•   Menu Bar       
•   Tool Bar

•   Status Bar

•   About Box      
•   Tips

•   Login Dialog

•   Master / Detail Content Area   Glazed Lists

    •Table and Fields

•   Wire it together with Actions 

•   Make calls to Web Services
Organization
             MVC Triad
Controller               View



                Model
Organization
             MVC Triad
Controller                View



                Model




                        Griffon Framework
Organization
Controller              View



              Model




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View



              Model




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View
                                    About Dialog


              Model




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View
                                    About Dialog

                                    Content Pane
              Model




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View
                                    About Dialog

                                    Content Pane
              Model
                                      Tool Bar




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View
                                    About Dialog

                                    Content Pane
              Model
                                      Tool Bar

                                     Status Bar




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View
                                    About Dialog

                                    Content Pane
              Model
                                      Tool Bar
 Services
                                     Status Bar




                      Griffon Framework
Organization
                                     Menu Bar
Controller              View
                                    About Dialog

                                    Content Pane
              Model
                                      Tool Bar
 Services
                                     Status Bar


Http Utils
   Get

                      Griffon Framework
   Put
  Post
 Delete
Organization
                                          Menu Bar
 Controller                  View
                                         About Dialog

                                         Content Pane
                   Model
                                           Tool Bar
   Services
                                          Status Bar


  Http Utils
     Get

                           Griffon Framework
     Put
    Post
   Delete




Rest Controller
Interaction with
RESTful WebServices
        SQL        HTTP       Grails
Action
       Method      Method   Convention
Create    INSERT    PUT       create
 Read     SELECT    GET       show
Update    UPDATE   POST       update
Delete    DELETE   DELETE     delete
Collect   SELECT               list
Loading Data
class GCollabTodoController {
...

  void loadData() {
      setStatus(quot;Loading Dataquot;)
      busy

        model.todos.clear()
        model.todos.addAll (TodoService.list(appContext))

      norm
      setStatus(quot;Finished Loading Dataquot;)
  }
                        class TodoService {
                             static String APP_URL = 'http://localhost:8080/collab-todo/json/todo'

                                static List list(appContext) {
                                     def userContext = appContext.userContext

                                    def get = new Get(url: APP_URL,
                                           userName: userContext.userName,
                                           password: userContext.password)

                                     def todoJson = get.text
                                    def str = JsonUtil.makeJSONStrict(todoJson)
                                    def jsonarray = JSONSerializer.toJSON(str)

                                     def todo
                                     def outputList = []
                                    jsonarray.each {
                                          todo = JsonUtil.jsonToObject(it.toString(), Todo.class)
                                          outputList.add todo
                                     }
                                     return outputList
                                }
Let’s Look at the Code
class Get{
    String url
    QueryString queryString = new QueryString()
    String text


                                         Get (HttpUtils)
    def userName
    def password

    String getText()
        try {
            def response
             def conn = new URL(toString()).openConnection()
             conn.requestMethod = quot;GETquot;
             conn.doOutput = true

            if (userName && password) {
                conn.setRequestProperty(quot;Authorizationquot;, quot;Basic ${userName}:${password}quot;)
            }

            def content
            if (conn.responseCode == conn.HTTP_OK) {
                 response = conn.content.text
            } else {
            response = quot;URL: quot; + this.toString() + quot;nquot; +
                 quot;RESPONSE CODE: quot; + conn.responseCode
                 throw new ResourceException(response)
             }
            conn.disconnect()
            return response
        } catch (Exception e) {
            println quot;Caught Exception ${e}quot;
            throw new ResourceException(e)
        }
    }

   String toString(){
        return url + quot;?quot; + queryString.toString()
   }
Save Todo
void saveTodo(event) {
     fillSelectedTodoFromView()

    def todo = selectedTodo()

     if(shouldBeSaved(todo)) {
         execWithExceptionHandling {
              TodoService.save(appContext, todo)
             loadData()
         }
    } else {
        JOptionPane.showMessageDialog(frame,
           quot;If this had been a completed application the Todo would have been updated:quot;)
    }
}

private boolean shouldBeSaved(todo) {
    if (todo.id == quot;quot; || !todo.id ) {
         return true
    }
    return false
}

private void fillSelectedTodoFromView() {
    selectedTodo().with {
        name = view.nameTextField?.text
        priority = view.priorityComboBox?.selectedItem
        selectedTodo().status = view.statusComboBox?.selectedItem
        completedDate = view.completedDateField?.date
        createdDate = view.createDateField?.date
        dueDate = view.dueDateField?.date
        note = view.noteTextField?.text
    }
}
Service Save
static void save(appContext, todo) {
         def userContext = appContext.userContext

        def put = new Put(url: APP_URL,
                 userName: userContext.userName,
               password: userContext.password)

        put.queryString.add(quot;namequot;, todo.name)
        put.queryString.add(quot;priorityquot;, todo.priority)
        put.queryString.add(quot;statusquot;, todo.status)
        put.queryString.add(quot;notequot;, todo.note)
        put.queryString.add(quot;owner.idquot;, userContext.id)
        put.queryString.addDate(quot;createdDatequot;, todo.createdDate)

        def putResponse = put.text
    }
package http.utils
class Put{
     String url
     QueryString queryString = new QueryString()
     String content



                                        Put (HttpUtils)
     String contentType
     String text
     def userName
     def password

    String getText(){
         def conn = new URL(url).openConnection()
         conn.setRequestMethod(quot;PUTquot; )
         conn.setRequestProperty(quot;Content-Typequot; , contentType)
         conn.doOutput = true
         conn.doInput = true

         if (userName && password) {
            conn.setRequestProperty(quot;Authorizationquot;, quot;Basic ${userName}:${password}quot;)
         }

         conn.outputStream.withWriter { out ->
           out.write(queryString.toString())
           out.flush()
           out.close()
         }

         def response
         if (conn.HTTP_OK == conn?.responseCode) {
           response = conn.content.text
         } else {
           response = quot;URL: quot; + this.toString() + quot;nquot; +
              quot;RESPONSE CODE: quot; + responseCode
         }
    conn.disconnect()
    return response
    }

    String toString(){
          return url + quot;?quot; + queryString.toString()
     }
}
RESTFul WebServices
class UserInfoController {
    def index = { redirect(action:show,params:params) }

    def show = {
          def result = session.user
          format(result)
     }

    def beforeInterceptor = {

         def authHeader = request.getHeader(quot;Authorizationquot;)
         if (authHeader) {
              def tokens = authHeader.split(' ')
              def user_password = tokens[1]
              tokens = user_password.split(':')
              def userid = tokens[0]
              def password = tokens[1]
              // At this point, the userid and password could be verified
              // to make sure that the person making the request is authenticated
              //
              // << AUTHENTICATION LOGIC / CALL >>
              //
              // Put look up the user object and put it into session for use
              // later by the controllers.
              def user = User.findByUserName(userid)
              if (user) {
                 session.user = user
              } else {
                 session.user = null
              }
         }
    }

    private format(obj) {
         def restType = (params.rest == quot;restquot;)?quot;XMLquot;:quot;JSONquot;
         println obj.quot;encodeAs$restTypequot;()
         render obj.quot;encodeAs$restTypequot;()
    }

}
class UserInfoController {
    def index = { redirect(action:show,params:params) }

    def show = {
          def result = session.user
          format(result)
     }

    def beforeInterceptor = {

         def authHeader = request.getHeader(quot;Authorizationquot;)
         if (authHeader) {
              def tokens = authHeader.split(' ')
              def user_password = tokens[1]
              tokens = user_password.split(':')
              def userid = tokens[0]
              def password = tokens[1]
              // At this point, the userid and password could be verified
              // to make sure that the person making the request is authenticated
              //
              // << AUTHENTICATION LOGIC / CALL >>
              //
              // Put look up the user object and put it into session for use
              // later by the controllers.
              def user = User.findByUserName(userid)
              if (user) {
                 session.user = user
              } else {
                 session.user = null
              }
         }
    }

    private format(obj) {
         def restType = (params.rest == quot;restquot;)?quot;XMLquot;:quot;JSONquot;
         println obj.quot;encodeAs$restTypequot;()
         render obj.quot;encodeAs$restTypequot;()
    }

}
Other Griffon Apps
http://github.com/jshingler/gcollabtodo/tree/master
Resources
•   Introduction to Groovy
•   Groovy Basics
•   More Advanced Groovy
•   Introduction to Grails
•   Building the User Interface
•   Building Domains and Services
•   Security in Grails
•   Web 2.0—Ajax and Friends
•   Web Services
•   Reporting
•   Batch Processing
•   Deploying and Upgrading
•   Alternative Clients
Resources
• Griffon
  • griffon.codehause.org
  • griffon-user@groovy.codehause.org
• Grails
  • www.grails.org
                                        Coming
• Books                                  Soon
Resources
• SwingLabs
  • swinglabs.org
• MigLayout
  • miglayout.org
• GlazedLists
  • publicobject.com/glazedlists
Conclusion
Thank You for your time

 •   Blog:
     http://jshingler.blogspot.com

 •   Email:
     ShinglerJim@gmail.com

 •   LinkedIn:
     http://www.linkedin.com/in/jimshingler

 •   Twitter:
     http://www.twitter.com/jshingler

Mais conteúdo relacionado

Semelhante a Griffon In Front Grails In Back

SAP WEBDYNPRO ABAP TRAINING
SAP WEBDYNPRO ABAP TRAININGSAP WEBDYNPRO ABAP TRAINING
SAP WEBDYNPRO ABAP TRAININGSanthosh Sap
 
Leveraging the Ribbon API and Dialog Framework
Leveraging the Ribbon API and Dialog FrameworkLeveraging the Ribbon API and Dialog Framework
Leveraging the Ribbon API and Dialog FrameworkCory Peters
 
JavaOne 2009 BOF-5189 Griffon In Depth
JavaOne 2009 BOF-5189 Griffon In DepthJavaOne 2009 BOF-5189 Griffon In Depth
JavaOne 2009 BOF-5189 Griffon In DepthDanno Ferrin
 
Accessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich ComponentsAccessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich ComponentsAchievers Tech
 
Windows Presentation Foundation
Windows Presentation FoundationWindows Presentation Foundation
Windows Presentation FoundationTran Ngoc Son
 
Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3
Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3
Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3Doris Chen
 
Cake php concept to deployment
Cake php concept to deploymentCake php concept to deployment
Cake php concept to deploymentilsanbao
 
Cake php 1 3 concept to deployment presentation
Cake php 1 3 concept to deployment presentationCake php 1 3 concept to deployment presentation
Cake php 1 3 concept to deployment presentationlaminbarrow
 
Beyond the Basics, Debugging with Firebug and Web Inspector
Beyond the Basics, Debugging with Firebug and Web InspectorBeyond the Basics, Debugging with Firebug and Web Inspector
Beyond the Basics, Debugging with Firebug and Web InspectorSteven Roussey
 
Django Developer Certification
Django Developer CertificationDjango Developer Certification
Django Developer CertificationVskills
 
Asp.Net MVC Framework Design Pattern
Asp.Net MVC Framework Design PatternAsp.Net MVC Framework Design Pattern
Asp.Net MVC Framework Design Patternmaddinapudi
 
SAP Inside Track 2010 - Thomas Jung Intro to WDA
SAP Inside Track 2010 - Thomas Jung Intro to WDASAP Inside Track 2010 - Thomas Jung Intro to WDA
SAP Inside Track 2010 - Thomas Jung Intro to WDAsjohannes
 
JavaScript and DOM Pattern Implementation
JavaScript and DOM Pattern ImplementationJavaScript and DOM Pattern Implementation
JavaScript and DOM Pattern Implementationdavejohnson
 
Behat internals for advanced usage. Symfony Camp 2016
Behat internals for advanced usage. Symfony Camp 2016Behat internals for advanced usage. Symfony Camp 2016
Behat internals for advanced usage. Symfony Camp 2016Sergey Polischook
 
Zend con 2016 bdd with behat for beginners
Zend con 2016   bdd with behat for beginnersZend con 2016   bdd with behat for beginners
Zend con 2016 bdd with behat for beginnersAdam Englander
 
The Google App Engine Oil Framework
The Google App Engine Oil FrameworkThe Google App Engine Oil Framework
The Google App Engine Oil FrameworkEric ShangKuan
 
MOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app developmentMOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app developmentanistar sung
 

Semelhante a Griffon In Front Grails In Back (20)

Sap webdynpro abap training
Sap webdynpro abap trainingSap webdynpro abap training
Sap webdynpro abap training
 
SAP WEBDYNPRO ABAP TRAINING
SAP WEBDYNPRO ABAP TRAININGSAP WEBDYNPRO ABAP TRAINING
SAP WEBDYNPRO ABAP TRAINING
 
Leveraging the Ribbon API and Dialog Framework
Leveraging the Ribbon API and Dialog FrameworkLeveraging the Ribbon API and Dialog Framework
Leveraging the Ribbon API and Dialog Framework
 
JavaOne 2009 BOF-5189 Griffon In Depth
JavaOne 2009 BOF-5189 Griffon In DepthJavaOne 2009 BOF-5189 Griffon In Depth
JavaOne 2009 BOF-5189 Griffon In Depth
 
Accessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich ComponentsAccessibility: A Journey to Accessible Rich Components
Accessibility: A Journey to Accessible Rich Components
 
Windows Presentation Foundation
Windows Presentation FoundationWindows Presentation Foundation
Windows Presentation Foundation
 
Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3
Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3
Building Beautiful and Interactive Metro apps with JavaScript, HTML5 & CSS3
 
Cake php concept to deployment
Cake php concept to deploymentCake php concept to deployment
Cake php concept to deployment
 
Cake php 1 3 concept to deployment presentation
Cake php 1 3 concept to deployment presentationCake php 1 3 concept to deployment presentation
Cake php 1 3 concept to deployment presentation
 
Beyond the Basics, Debugging with Firebug and Web Inspector
Beyond the Basics, Debugging with Firebug and Web InspectorBeyond the Basics, Debugging with Firebug and Web Inspector
Beyond the Basics, Debugging with Firebug and Web Inspector
 
Django Developer Certification
Django Developer CertificationDjango Developer Certification
Django Developer Certification
 
Asp.Net Mvc Dev Days09
Asp.Net Mvc Dev Days09Asp.Net Mvc Dev Days09
Asp.Net Mvc Dev Days09
 
Asp.Net MVC Framework Design Pattern
Asp.Net MVC Framework Design PatternAsp.Net MVC Framework Design Pattern
Asp.Net MVC Framework Design Pattern
 
SAP Inside Track 2010 - Thomas Jung Intro to WDA
SAP Inside Track 2010 - Thomas Jung Intro to WDASAP Inside Track 2010 - Thomas Jung Intro to WDA
SAP Inside Track 2010 - Thomas Jung Intro to WDA
 
JavaScript and DOM Pattern Implementation
JavaScript and DOM Pattern ImplementationJavaScript and DOM Pattern Implementation
JavaScript and DOM Pattern Implementation
 
Behat internals for advanced usage. Symfony Camp 2016
Behat internals for advanced usage. Symfony Camp 2016Behat internals for advanced usage. Symfony Camp 2016
Behat internals for advanced usage. Symfony Camp 2016
 
Zend con 2016 bdd with behat for beginners
Zend con 2016   bdd with behat for beginnersZend con 2016   bdd with behat for beginners
Zend con 2016 bdd with behat for beginners
 
Appengine Nljug
Appengine NljugAppengine Nljug
Appengine Nljug
 
The Google App Engine Oil Framework
The Google App Engine Oil FrameworkThe Google App Engine Oil Framework
The Google App Engine Oil Framework
 
MOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app developmentMOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app development
 

Mais de Jim Shingler

HIPAA Solutions on Cloud Foundry
HIPAA Solutions on Cloud FoundryHIPAA Solutions on Cloud Foundry
HIPAA Solutions on Cloud FoundryJim Shingler
 
DevOps is a Journey, Not an Event
DevOps is a Journey, Not an EventDevOps is a Journey, Not an Event
DevOps is a Journey, Not an EventJim Shingler
 
Personal Healthcare IOT on PCF using Spring
Personal Healthcare IOT on PCF using SpringPersonal Healthcare IOT on PCF using Spring
Personal Healthcare IOT on PCF using SpringJim Shingler
 
S1 2GX 2011 - Content Management with a Custom CMS
S1 2GX 2011 - Content Management with a Custom CMS S1 2GX 2011 - Content Management with a Custom CMS
S1 2GX 2011 - Content Management with a Custom CMS Jim Shingler
 
S1 2GX 2011 - Using Grails on a public facing Fortune 500 website
S1 2GX 2011 - Using Grails on a public facing  Fortune 500 website S1 2GX 2011 - Using Grails on a public facing  Fortune 500 website
S1 2GX 2011 - Using Grails on a public facing Fortune 500 website Jim Shingler
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Jim Shingler
 

Mais de Jim Shingler (6)

HIPAA Solutions on Cloud Foundry
HIPAA Solutions on Cloud FoundryHIPAA Solutions on Cloud Foundry
HIPAA Solutions on Cloud Foundry
 
DevOps is a Journey, Not an Event
DevOps is a Journey, Not an EventDevOps is a Journey, Not an Event
DevOps is a Journey, Not an Event
 
Personal Healthcare IOT on PCF using Spring
Personal Healthcare IOT on PCF using SpringPersonal Healthcare IOT on PCF using Spring
Personal Healthcare IOT on PCF using Spring
 
S1 2GX 2011 - Content Management with a Custom CMS
S1 2GX 2011 - Content Management with a Custom CMS S1 2GX 2011 - Content Management with a Custom CMS
S1 2GX 2011 - Content Management with a Custom CMS
 
S1 2GX 2011 - Using Grails on a public facing Fortune 500 website
S1 2GX 2011 - Using Grails on a public facing  Fortune 500 website S1 2GX 2011 - Using Grails on a public facing  Fortune 500 website
S1 2GX 2011 - Using Grails on a public facing Fortune 500 website
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106
 

Último

Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????blackmambaettijean
 

Último (20)

Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????
 

Griffon In Front Grails In Back

  • 1. Griffon in Front Grails in Back Leveraging Grails with Griffon
  • 2. Griffon in Front Grails in Back Leveraging Grails with Griffon
  • 3. Abstract Groovy and Grails have given us the ability to leverage the strength of the Java Platform (and Eco System) and the productivity of “Convention over Configuration” to construct websites. But “What If” the User Interface requirements of the new application is best solved with the type of interaction a desktop application provides? Griffon bring the same productivity gains to the desktop application that Grails brings to web applications. This session will use Griffon and popular open source libraries to build a desktop applicaiton to interact with a Grails backend.
  • 4. Introduction My name is Jim Shingler Chief Technical Architect President of Genuine Solutions Beginning Groovy and Grails Co-Author FallME (Inversion of Control for JavaME) Co-Founder
  • 6. Griffon Founders Danno Ferrin http://shemnon.com/speling Andres Almiray http://jroller.com/aalmiray James Williams http://jameswilliams.be/blog
  • 7. Common Complaints about Swing Its hard Have to code too much Code is complex Not many Advanced GUI Components (Myth)
  • 8. Start Small • Swing and SwingX Builder • GUI Components • About Box • Define and Process Actions
  • 9. Builders The Builder Pattern is a software design pattern. The intention is to separate the construction of a complex object from its representation so that the same construction process can create different representations. Often, the Build Patter is used to build Products in accordance to the Composite pattern. Swing is a complex hierarchy, . . . Source: Wikipedia Sounds like an opportunity
  • 10. GUI Builders • SwingBuilder Applies the Builder Pattern to the construction of Swing “Makes Swing Groovy” • SwingXBuilder Extends SwingBuilder adding the SwingLabs Components • JideBuilder Extends SwingBuilder adding JIDE Components • SWTBuilder Applies the Builder Pattern to the construction of SWT “Makes SWT Groovy”
  • 11. Plugins A plugin is a Griffon extension point. Conceptually, it is similar to the plugins found in modern IDEs. A plugin is a technique to encapsulate functionality that can be reused across multiple applications. Griffon’s plugin community has just begun but it is growing fast. See: >griffon list-plugins http://grails.org/Plugins http://www.grails.org/The+Plug-in+Developers+Guide
  • 12. DEMO • Create Count App • Add Button • Build and Initialize “Click Action” • Process the Click Action • Install and Enable SwingXBuilder • Build and Initialize Menus • Build and Initialize “Menu Actions” • Process the Menu Actions
  • 13. DEMO • Create Count App • Add Button • Build and Initialize “Click Action” • Process the Click Action • Install and Enable SwingXBuilder • Build and Initialize Menus • Build and Initialize “Menu Actions” • Process the Menu Actions NOTE: The Code included on the next several pages has been enhanced based upon questions asked in the session.
  • 14. Controller import javax.swing.JOptionPane class Sample2Controller { // these will be injected by Griffon def model def view void mvcGroupInit(Map args) { // this method is called after model and view are injected } def click = { evt = null -> model.count++ } def exit = { evt = null -> System.exit(0) } def showAbout = { evt = null -> JOptionPane.showMessageDialog(null, '''This is the SimpleUI Application''') } }
  • 16. View application(title:'sample2', /*size:[320,480], */location:[200,200], pack:true, locationByPlatform:false) { // add content here build(Actions) build(MenuBar) button(id:'clickButton', text:bind{ model.count }, action: clickAction) } MenuBar jxmenuBar { menu(text: 'File', mnemonic: 'F') { menuItem(exitAction) } glue() menu(text: 'Help', mnemonic: 'H') { menuItem(aboutAction) } }
  • 17. Actions // create the actions action(id: 'clickAction', name: 'Click Me', closure: controller.&click, shortDescription: 'Increment the Click Count' ) action(id: 'exitAction', name: 'Exit', closure: controller.exit, mnemonic: 'x', accelerator: 'F4', shortDescription: 'Exit SimpleUI' ) action(id: 'aboutAction', name: 'About', closure: controller.showAbout, mnemonic: 'A', accelerator: 'F1', shortDescription: 'Find out about SimpleUI' )
  • 18. Goal
  • 19. Goal Menu Bar Tool Bar Content Area Status Bar
  • 20. Goal Menu Bar Tool Bar Login Dialog Content Area Tips Dialog Status Bar
  • 21. Planning • Menu Bar • Tool Bar • Status Bar • About Box • Tips • Login Dialog • Master / Detail Content Area •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 22. Planning • Menu Bar  • Tool Bar • Status Bar • About Box • Tips • Login Dialog • Master / Detail Content Area •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 23. Planning • Menu Bar  • Tool Bar • Status Bar • About Box • Tips • Login Dialog • Master / Detail Content Area •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 24. Planning • Menu Bar  • Tool Bar • Status Bar • About Box  • Tips • Login Dialog • Master / Detail Content Area •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 25. Planning • Menu Bar  • Tool Bar • Status Bar • About Box  • Tips • Login Dialog • Master / Detail Content Area •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 26. Planning • Menu Bar  • Tool Bar • Status Bar • About Box  • Tips • Login Dialog • Master / Detail Content Area Glazed Lists •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 27. Planning • Menu Bar  • Tool Bar • Status Bar • About Box  • Tips • Login Dialog • Master / Detail Content Area Glazed Lists •Table and Fields • Wire it together with Actions • Make calls to Web Services
  • 28. Planning • Menu Bar  • Tool Bar • Status Bar • About Box  • Tips • Login Dialog • Master / Detail Content Area Glazed Lists •Table and Fields • Wire it together with Actions  • Make calls to Web Services
  • 29. Planning • Menu Bar  • Tool Bar • Status Bar • About Box  • Tips • Login Dialog • Master / Detail Content Area Glazed Lists •Table and Fields • Wire it together with Actions  • Make calls to Web Services
  • 30. Organization MVC Triad Controller View Model
  • 31. Organization MVC Triad Controller View Model Griffon Framework
  • 32. Organization Controller View Model Griffon Framework
  • 33. Organization Menu Bar Controller View Model Griffon Framework
  • 34. Organization Menu Bar Controller View About Dialog Model Griffon Framework
  • 35. Organization Menu Bar Controller View About Dialog Content Pane Model Griffon Framework
  • 36. Organization Menu Bar Controller View About Dialog Content Pane Model Tool Bar Griffon Framework
  • 37. Organization Menu Bar Controller View About Dialog Content Pane Model Tool Bar Status Bar Griffon Framework
  • 38. Organization Menu Bar Controller View About Dialog Content Pane Model Tool Bar Services Status Bar Griffon Framework
  • 39. Organization Menu Bar Controller View About Dialog Content Pane Model Tool Bar Services Status Bar Http Utils Get Griffon Framework Put Post Delete
  • 40. Organization Menu Bar Controller View About Dialog Content Pane Model Tool Bar Services Status Bar Http Utils Get Griffon Framework Put Post Delete Rest Controller
  • 41. Interaction with RESTful WebServices SQL HTTP Grails Action Method Method Convention Create INSERT PUT create Read SELECT GET show Update UPDATE POST update Delete DELETE DELETE delete Collect SELECT list
  • 42. Loading Data class GCollabTodoController { ... void loadData() { setStatus(quot;Loading Dataquot;) busy model.todos.clear() model.todos.addAll (TodoService.list(appContext)) norm setStatus(quot;Finished Loading Dataquot;) } class TodoService { static String APP_URL = 'http://localhost:8080/collab-todo/json/todo' static List list(appContext) { def userContext = appContext.userContext def get = new Get(url: APP_URL, userName: userContext.userName, password: userContext.password) def todoJson = get.text def str = JsonUtil.makeJSONStrict(todoJson) def jsonarray = JSONSerializer.toJSON(str) def todo def outputList = [] jsonarray.each { todo = JsonUtil.jsonToObject(it.toString(), Todo.class) outputList.add todo } return outputList }
  • 43. Let’s Look at the Code
  • 44. class Get{ String url QueryString queryString = new QueryString() String text Get (HttpUtils) def userName def password String getText() try { def response def conn = new URL(toString()).openConnection() conn.requestMethod = quot;GETquot; conn.doOutput = true if (userName && password) { conn.setRequestProperty(quot;Authorizationquot;, quot;Basic ${userName}:${password}quot;) } def content if (conn.responseCode == conn.HTTP_OK) { response = conn.content.text } else { response = quot;URL: quot; + this.toString() + quot;nquot; + quot;RESPONSE CODE: quot; + conn.responseCode throw new ResourceException(response) } conn.disconnect() return response } catch (Exception e) { println quot;Caught Exception ${e}quot; throw new ResourceException(e) } } String toString(){ return url + quot;?quot; + queryString.toString() }
  • 45. Save Todo void saveTodo(event) { fillSelectedTodoFromView() def todo = selectedTodo() if(shouldBeSaved(todo)) { execWithExceptionHandling { TodoService.save(appContext, todo) loadData() } } else { JOptionPane.showMessageDialog(frame, quot;If this had been a completed application the Todo would have been updated:quot;) } } private boolean shouldBeSaved(todo) { if (todo.id == quot;quot; || !todo.id ) { return true } return false } private void fillSelectedTodoFromView() { selectedTodo().with { name = view.nameTextField?.text priority = view.priorityComboBox?.selectedItem selectedTodo().status = view.statusComboBox?.selectedItem completedDate = view.completedDateField?.date createdDate = view.createDateField?.date dueDate = view.dueDateField?.date note = view.noteTextField?.text } }
  • 46. Service Save static void save(appContext, todo) { def userContext = appContext.userContext def put = new Put(url: APP_URL, userName: userContext.userName, password: userContext.password) put.queryString.add(quot;namequot;, todo.name) put.queryString.add(quot;priorityquot;, todo.priority) put.queryString.add(quot;statusquot;, todo.status) put.queryString.add(quot;notequot;, todo.note) put.queryString.add(quot;owner.idquot;, userContext.id) put.queryString.addDate(quot;createdDatequot;, todo.createdDate) def putResponse = put.text }
  • 47. package http.utils class Put{ String url QueryString queryString = new QueryString() String content Put (HttpUtils) String contentType String text def userName def password String getText(){ def conn = new URL(url).openConnection() conn.setRequestMethod(quot;PUTquot; ) conn.setRequestProperty(quot;Content-Typequot; , contentType) conn.doOutput = true conn.doInput = true if (userName && password) { conn.setRequestProperty(quot;Authorizationquot;, quot;Basic ${userName}:${password}quot;) } conn.outputStream.withWriter { out -> out.write(queryString.toString()) out.flush() out.close() } def response if (conn.HTTP_OK == conn?.responseCode) { response = conn.content.text } else { response = quot;URL: quot; + this.toString() + quot;nquot; + quot;RESPONSE CODE: quot; + responseCode } conn.disconnect() return response } String toString(){ return url + quot;?quot; + queryString.toString() } }
  • 48.
  • 50. class UserInfoController { def index = { redirect(action:show,params:params) } def show = { def result = session.user format(result) } def beforeInterceptor = { def authHeader = request.getHeader(quot;Authorizationquot;) if (authHeader) { def tokens = authHeader.split(' ') def user_password = tokens[1] tokens = user_password.split(':') def userid = tokens[0] def password = tokens[1] // At this point, the userid and password could be verified // to make sure that the person making the request is authenticated // // << AUTHENTICATION LOGIC / CALL >> // // Put look up the user object and put it into session for use // later by the controllers. def user = User.findByUserName(userid) if (user) { session.user = user } else { session.user = null } } } private format(obj) { def restType = (params.rest == quot;restquot;)?quot;XMLquot;:quot;JSONquot; println obj.quot;encodeAs$restTypequot;() render obj.quot;encodeAs$restTypequot;() } }
  • 51. class UserInfoController { def index = { redirect(action:show,params:params) } def show = { def result = session.user format(result) } def beforeInterceptor = { def authHeader = request.getHeader(quot;Authorizationquot;) if (authHeader) { def tokens = authHeader.split(' ') def user_password = tokens[1] tokens = user_password.split(':') def userid = tokens[0] def password = tokens[1] // At this point, the userid and password could be verified // to make sure that the person making the request is authenticated // // << AUTHENTICATION LOGIC / CALL >> // // Put look up the user object and put it into session for use // later by the controllers. def user = User.findByUserName(userid) if (user) { session.user = user } else { session.user = null } } } private format(obj) { def restType = (params.rest == quot;restquot;)?quot;XMLquot;:quot;JSONquot; println obj.quot;encodeAs$restTypequot;() render obj.quot;encodeAs$restTypequot;() } }
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 59. Resources • Introduction to Groovy • Groovy Basics • More Advanced Groovy • Introduction to Grails • Building the User Interface • Building Domains and Services • Security in Grails • Web 2.0—Ajax and Friends • Web Services • Reporting • Batch Processing • Deploying and Upgrading • Alternative Clients
  • 60. Resources • Griffon • griffon.codehause.org • griffon-user@groovy.codehause.org • Grails • www.grails.org Coming • Books Soon
  • 61. Resources • SwingLabs • swinglabs.org • MigLayout • miglayout.org • GlazedLists • publicobject.com/glazedlists
  • 62. Conclusion Thank You for your time • Blog: http://jshingler.blogspot.com • Email: ShinglerJim@gmail.com • LinkedIn: http://www.linkedin.com/in/jimshingler • Twitter: http://www.twitter.com/jshingler