Introduction to Griffon


Published on

Introduction to Griffon Workshop
Presented at GR8Conf USA
on 27 June 2011

Published in: Technology, Education
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Introduction to Griffon

  1. 1. Introduction to Griffon James Williams
  2. 2. About Me● Co-founder of Griffon● Author of "Learning HTML5 Game Programming"● Blog:● Twitter: @ecspike
  3. 3. Agenda ● What is Griffon? ● Common Griffon Commands ● Model - View - Controller ● MigLayout ● Binding ● Plugins ● Validation ● Lots of codeCode for this demo:
  4. 4. What is Griffon?● Apache 2 Licensed●● Inspired by Grails and the Swing Application Framework● Extensible with plugins and addons● Deployable to Webstart, Applet, or single jar file● Now to Github!
  5. 5. Common Griffon Commands● create-app● create-mvc● test-app● run-app● help● package● Form: griffon <environment> command <options>
  6. 6. Griffon Aliases ● create-app ● create-mvc => cM ● test-app => tA ● run-app => app ● help => h ● package => pYou can even create your own with the create-alias command.
  7. 7. Griffon Views● Represent the presentation layer of the MVC triad● Use Domain Specific Languages called UI Builders● SwingBuilder is bundled● Additional builder available as plugins (SwingXBuilder, JIDE, MacWidgets, etc)
  8. 8. Sample Griffon View Filepackage helloapplication(title: Hello, preferredSize: [320, 240], pack: true, //location: [50,50], locationByPlatform:true, iconImage: imageIcon(/griffon-icon-48x48.png).image, iconImages: [/* truncated */]) { // add content here label(Content Goes Here) // delete me}
  9. 9. Codecd <GRIFFON INSTALL DIR>cd samples/SwingPadgriffon app
  10. 10. Common Widgets● label● button● checkBox, radioButton● textField● textArea● panel● hbox, vbox
  11. 11. Codeimport javax.swing.JOptionPanebutton(Click, actionPerformed: {JOptionPane.showMessageDialog( null, "Button clicked at ${new Date()}" )})
  12. 12. Crash course in MigLayout● Grid-based LayoutManager● Human-readable● Docking (BorderLayout)● Absolute Positioning● Constraints● Units (px, mm, cm, pts, in, %)
  13. 13. Crash course in MigLayout● wrap, newline● w/width, h/height● Docking (BorderLayout)● gap● span● cell [column] [row]
  14. 14. Codeimport net.miginfocom.swing.MigLayoutpanel(layout:new MigLayout()) {label(text:label)label(text:Cell 1 1 , constraints:cell 1 1)}
  15. 15. Codeimport net.miginfocom.swing.MigLayoutpanel(layout:new MigLayout()) {label(text:Text, constraints:wrap)label(text:Text2 )label(text:Text3, constraints:w 50px, newline)label(text:Text4 )textField(columns:10, constraints:span 2, newline)}
  16. 16. Griffon Models● Hold data for the MVC triad● Can send/receive(@Bindable) data to/from widgets in the view● Can do Grails-style validation (plugin)
  17. 17. Sample Griffon Model fileimport groovy.beans.Bindableclass HelloModel { // @Bindable String propName}
  18. 18. Binding FormstextField(text:bind{})textField(text:bind(source:model, sourceProperty:"data"))textField(text:bind(target:model, targetProperty:"data"))
  19. 19. Griffon Controllers ● Brains of the MVC triad ● Contain actions for the triad ● Injected with references to the model and view
  20. 20. Sample Griffon Controller Fileclass HelloController { // these will be injected by Griffon def model def view // void mvcGroupInit(Map args) { // // this method is called after model and view are injected // } // void mvcGroupDestroy() { // // this method is called when the group is destroyed // }
  21. 21. Griffon Plugins● Extend app functionality at runtime or compile-time● Can be as simple as adding a couple jars...● Or as complex as creating a custom builder to use them● Common areas: ○ UI Toolkits ○ Dependency Injection ○ Persistence
  22. 22. Code(From inside a Griffon app directory)griffon list-pluginsgriffon lPgriffon plugin-info <name of plugin>
  23. 23. Creating A ToDo Application
  24. 24. Codegriffon create-app TodoApp<wait... wait... wait...>griffon install-plugin swingx-buildergriffon install-plugin swingxtras-buildergriffon install-plugin validation
  25. 25. Code (/src/main/todoapp/...)class TodoItem {String todoTextDate dueDateList <String>tags Boolean isDoneBoolean isSavedpublic tagsToString() {Arrays.sort(tags)tags.join(,)}}
  26. 26. Code (/griffon-app/models/todoapp/.)import groovy.beans.Bindableclass TodoModel {@Bindable String todoText@Bindable Date dueDate@Bindable tagsTextList <TodoItem> todos}
  27. 27. Code (griffon-app/views/todoapp/...)size: [640,480],locationByPlatform:true,layout: new MigLayout()) { // Add Todo field jxtextField(id:todoField, columns:45,text:bind(target:model, todoText),constraints:w 90%) promptSupport(todoField, prompt: "Add Todo, type here") button(id:btnAdd, text:Add, constraints:w 10%, wrap)}
  28. 28. Code (griffon-app/views/todoapp/...)// More options (date, tags, etc)jxtaskPane(title:More Options, constraints: w 100%, spanx 2, wrap, layout:new MigLayout()) {label(text:Due Date:, constraints:wrap)jxdatePicker(id:datePicker, date:bind(target:model,dueDate),constraints:wrap)label(text:Tags, constraints:wrap)textField(id:tagsField, columns:40, text:bind(target:model, tagsText))promptSupport(tagsField, prompt:Enter tags comma separated)}
  29. 29. Swing isnt slow, ...
  30. 30. ... devs are just coding it badly
  31. 31. Dont do this!JButton b = new JButton("Run query");b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { runQueries(); }});
  32. 32. Threading in Swing● All updates to the UI must happen on the single Event Dispatcher Thread(EDT)● Nothing else should be executed on the EDT*● Bad dev starts long running task, UI locks up until it is done● SwingUtilities provides the following functions: ○ isEventDispatchThread() ○ invokeLater(Runnable), invoke Runnable when convenient ○ invokeAndWait(Runnable) invoke when convenient and wait for completion
  33. 33. (Swing) Threading in Griffon● Encapsulates SwingUtilities and simple threading● Adds closure for running a task outside the EDT● Available closures: ○ doLater { ... } ==> SwingUtilities.doLater ○ edt{ ...} ==> SwingUtilities.invokeAndWait ○ doOutside{..} ==> Spawns new Thread if inside EDT● Griffon 0.9.2+ encloses controller actions with doOutside by default (can be turned off)
  34. 34. Code (griffon-app/controllers/...)def load = {doOutside {def todos = model.derby.all()todos.each {def todo = TodoItem.fromMap(it)model.todos.add(todo)}}}
  35. 35. Adding a table of todo items
  36. 36. The old way ... ● A very brittle TableModel ● Hard to update and sort items ● Hard to map between POJOs and rows
  37. 37. Code (SwingPad)import javax.swing.table.DefaultTableModeldef a = new DefaultTableModel([A,B,C] as String[], 0)a.addRow(1,2,3)a.addRow(4,5,6)a.addRow(7,8,9)scrollPane {jxtable(model:a)}
  38. 38. Code (griffon-app/views/todoapp/...)...jxtitledPanel(title:Tasks) {scrollPane {jxtable(id:table, model:model.todoTableModel)}}
  39. 39. GlazedLists● Easy data model management for: ○ JComboBox, ○ JList ○ JTable● Provides easy dynamic sorting and filtering● Supports both SWT and Swing● Supports concurrency● Link:
  40. 40. BasicEventList● Compatible with ArrayList and Vector● Stores the items for your List or Table● Parameterized around POJO BasicEventList todos = new BasicEventList<TodoItem>()● Adds listeners for changes in the list
  41. 41. BasicEvent<Widget>Model● Receives changes from BasicEventList and pushes them to the widget● BasicEventListModel and BasicEventComboModel take only a BasicEventList (POJOs to display) as a parameter● JTables require a bit more definition
  42. 42. TableFormat● Interface that defines how the table is displayed in the GUI● By default is not editable after initialization● Required functions: ○ getColumnCount() ○ getColumnName(int) ○ getColumnValue(obj, int)
  43. 43. WritableTableFormat● Interface allowing editing of fields● Can set granular edit policy with isEditable● Uses default field editor (usually a JTextField)● Required functions: ○ isEditable(obj, column) ○ setColumnValue(obj, value, column)
  44. 44. AdvancedTableFormat● Interface that allows custom cell renderers For example: DatePickers, Check boxes, Sliders, etc.● JTables set policies on how to render a cell with a certain class type● JXTables do sorting and filtering so a separate comparator is not needed● Required functions: ○ getColumnClass(int) ○ getColumnComparator(int)
  45. 45. Adding a bit of Swing glue:Table Editors and Renderers
  46. 46. TableEditor vs TableRenderers● Any Swing component can be an editor or renderer● TableEditors show when that cell is being edited● TableRenderers appear in all other cases● Can be mixed an matched For example: a default renderer but a specialized renderer
  47. 47. Default TableRenderers Class Renderer Component Boolean* JCheckBox* Number JLabel, right-aligned Double, Float JLabel, right-aligned with coersion to String using NumberFormat Date JLabel, with coersion to String using DateFormat Object JLabel with objects toString() value
  48. 48. Code (src/main/groovy)class CheckBoxRenderer extends JCheckBox implements TableCellRenderer {public Component getTableCellRendererComponent(JTable table, Objectvalue, boolean isSelected, boolean hasFocus, int row, int column) {if (isSelected) {setForeground(table.getSelectionForeground())setBackground(table.getSelectionBackground())} else {setForeground(table.getForeground())setBackground(table.getBackground())}setSelected(value)return this}}
  49. 49. Code (src/main/groovy)class DatePickerEditor extends AbstractCellEditor implements TableCellEditor {def component = new JXDatePicker()public Component getTableCellEditorComponent(JTable table, Object value,boolean isSelected, int row, int column) {component.setDate(value)return component}public Object getCellEditorValue() {return component.getDate()}}
  50. 50. Code (griffon-app/controllers/...)// setup renderers and editorsview.table.setDefaultRenderer(Boolean.class, new CheckBoxRenderer())view.table.setDefaultEditor(Boolean.class, new DefaultCellEditor(newJCheckBox()))view.table.setDefaultEditor(Date.class, new DatePickerEditor())
  51. 51. Code (griffon-app/controllers/...)// table model listenermodel.todoTableModel.addTableModelListener([tableChanged:{evt ->def i = evt.getFirstRow()def j = evt.getLastRow()if (i == j && evt.getType() == TableModelEvent.UPDATE) {// do something with the update date}}] as TableModelListener)
  52. 52. Code (griffon-app/controllers/...)def deleteCompleted = { def lock = model.todos.getReadWriteLock().writeLock() lock.lock() def list = model.todos.findAll{item -> item.isDone == true } list.each { item -> model.derby.remove( model.todos.remove(item) } lock.unlock() }
  53. 53. Saving to disk
  54. 54. Introducing Deckchair● Modeled after Lawnchair, a lightweight JSON store (JS)● Written in Groovy● Uses an adapter-implementation model for persistence Derby is the only implemented backend for Deckchair● Provides more flexibility than direct use of backends● Link:
  55. 55. Deckchair API● save, saves a single object● get, retreives an object● each, executes code for every item in the result set● find, finds objects using a closure● all, returns all objects in the set● nuke, destroys all objects in the set● remove, destroys a single object
  56. 56. Deckchair Derby Internals● Creates a table with: ○ id, 36-character VARCHAR ○ timestamp, BIGINT ○ value, LONG VARCHAR (about 32,000 characters)● Objects are serialized to JSON strings on insertion● Only the uniqueness of the id field is enforced● Good for small amounts of data and demos● Good for data models that arent exactly pinned down yet
  57. 57. Code// from modeldef derby = new Deckchair([name:todos, adaptor:derby])//loading todosdef todos = model.derby.all()// save function with optional closure to print status after, {obj -> println obj; println "saved todo"})
  58. 58. Validation
  59. 59. GValidation Plugin● Provides Grails style validation and messages● Uses a Grails-like DSL● Supports custom constraints in addition to built-in types● Provides visual cues for incorrect data● Can internationalize error messages with the Griffon i18n plugin
  60. 60. Code (griffon-app/models/...)package todoimport jwill.deckchair.*import groovy.beans.Bindable//...@Validatableclass TodoModel {@Bindable String todoText@Bindable Date dueDate@Bindable tagsText//...static constraints = {todoText(blank:false)dueDate(nullable:true)tagsText(blank:true)}}
  61. 61. Supported Validators● blank ● min● creditCard ● minSize● email ● notEqual● inetAddress ● nullable● inList ● range● matches ● size● max ● url
  62. 62. Validation an objectdef addItem = {if (model.validate()) {// save file} catch(Exception ex) { }} else {model.errors.each{error ->println error}}}
  63. 63. Showing a error componentjxtextField( id:todoField, columns:45, text:bind(target:model, todoText), constraints:w 90%, errorRenderer:for: todoText, styles: [highlight, popup])
  64. 64. Printing in Griffon
  65. 65. There once was a project called EasyPrint
  66. 66. It was a casualty to the Java. net/Kenai conversion :(
  67. 67. From HTML to PDF
  68. 68. Code (griffon-app/controllers/...)def builder = new MarkupBuilder(writer)builder.html {head{title(My Todo List)}body {h2("My Todo List")br {}table (width:90%, border:1, border-spacing:0){tr { td(Completed); td(Description); td(Due Date); td(Tags) }model.todos.each { item ->tr {if (item.isDone) { td(Done) } else { td() }td(item.todoText)td(item.dueDate)td(item.tagsToString())}}}}}
  69. 69. Code (griffon-app/controllers/...)def createPDF = { createHTML() def file = File.createTempFile("todos","txt"); file.write(writer.toString()) def docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() def doc = docBuilder.parse(file) def renderer = new ITextRenderer() renderer.setDocument(doc, null) def outputFile = "todos.pdf" def os = new FileOutputStream(outputFile) renderer.layout(); renderer.createPDF(os); os.close() edt { JOptionPane.showMessageDialog(null, "PDF created.") } }
  70. 70. Questions ?