Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

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 ?