Advertisement
Guest User

Groovy, Grails and Vaadin

a guest
Dec 6th, 2011
417
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.32 KB | None | 0 0
  1. by [[http://vaadin.com/petter|Petter Holmström]]
  2.  
  3. == Introduction ==
  4.  
  5. The purpose of this article is to briefly demonstrate how a Vaadin user interface can be added to a Grails application. Prerequisites:
  6.  
  7. * Knowledge of [[http://groovy.codehaus.org|Groovy]], [[http://grails.org|Grails]] and Vaadin
  8. * A working installation of Grails 1.3.0.RC1 or later
  9. * A Groovy code editor, or an IDE with Grails support
  10.  
  11. == Setting up the demo application ==
  12.  
  13. In this article, we are going to add a Vaadin user interface to the application described in [[https://www.ibm.com/developerworks/java/library/j-grails01158/|this tutorial]].
  14.  
  15. Start by creating a new Grails project:
  16.  
  17. {{{$ grails create-app tripplanner}}}
  18.  
  19. Change into the //tripplanner// directory and create a domain class:
  20.  
  21. {{{$ grails create-domain-class Trip}}}
  22.  
  23. Open //grails-app/domain/tripplanner/Trip.groovy// and add some fields:
  24.  
  25. {{{
  26. package tripplanner
  27.  
  28. class Trip {
  29.  
  30. static constraints = {
  31. }
  32.  
  33. String name
  34. String city
  35. Date startDate
  36. Date endDate
  37. String purpose
  38. String notes
  39. }
  40. }}}
  41.  
  42. Then generate a controller and corresponding views:
  43.  
  44. {{{$ grails generate-all tripplanner.Trip}}}
  45.  
  46. You can now start the application and try it out:
  47.  
  48. {{{$ grails run-app}}}
  49.  
  50. == Creating the Vaadin UI ==
  51.  
  52. Now, [[http://vaadin.com/download|download]] the latest Vaadin jar and place it in the //lib// directory. Next, create a new class named {{{tripplanner.VaadinApp}}} and place it in the //src/groovy// directory. The complete implementation of the class is as follows:
  53.  
  54. {{{
  55. package tripplanner
  56.  
  57. import com.vaadin.ui.*
  58. import com.vaadin.data.*
  59. import com.vaadin.data.util.*
  60.  
  61. class VaadinApp extends com.vaadin.Application {
  62.  
  63. void init() {
  64. def window = new Window("Hello Vaadin!", new SplitPanel(SplitPanel.ORIENTATION_HORIZONTAL))
  65. setMainWindow window
  66.  
  67. // Trip editor
  68.  
  69. def tripEditor = new Form()
  70. tripEditor.setSizeFull()
  71. tripEditor.layout.setMargin true
  72. tripEditor.immediate = true
  73. tripEditor.visible = false
  74.  
  75. def saveButton = new Button("Save", new Button.ClickListener() {
  76. void buttonClick(Button.ClickEvent event) {
  77. Trip.withTransaction { status ->
  78. def tripInstance = tripEditor.itemDataSource.bean
  79. if (!tripInstance.save(flush:true)) {
  80. window.showNotification "Could not save changes"
  81. } else {
  82. window.showNotification "Changes saved"
  83. }
  84. }
  85. }
  86. })
  87.  
  88. tripEditor.footer = new HorizontalLayout()
  89. tripEditor.footer.addComponent saveButton
  90.  
  91. // Trip browser
  92.  
  93. def container = new BeanItemContainer<Trip>(Trip.class)
  94. Trip.list().each {
  95. container.addBean it
  96. }
  97.  
  98. def browser = new VerticalLayout()
  99. browser.setSizeFull()
  100.  
  101. def table = new Table()
  102. table.containerDataSource = container
  103. table.selectable = true
  104. table.setSizeFull()
  105. table.visibleColumns = ["name", "purpose", "startDate", "endDate", "city", "notes"]
  106. table.immediate = true
  107. table.addListener new Property.ValueChangeListener() {
  108. void valueChange(Property.ValueChangeEvent event) {
  109. if (table.value) {
  110. tripEditor.itemDataSource = table.getItem(table.value)
  111. tripEditor.visibleItemProperties = ["name", "purpose", "startDate", "endDate", "city", "notes"]
  112. } else {
  113. tripEditor.itemDataSource = null
  114. }
  115. tripEditor.visible = tripEditor.itemDataSource != null
  116. }
  117. }
  118.  
  119. browser.addComponent table
  120.  
  121. def toolbar = new HorizontalLayout()
  122.  
  123. def addButton = new Button("+", new Button.ClickListener() {
  124. void buttonClick(Button.ClickEvent event) {
  125. Trip.withTransaction { status ->
  126. def tripInstance = new Trip()
  127. tripInstance.name = "A new trip"
  128. tripInstance.endDate = new Date()
  129. tripInstance.startDate = new Date()
  130. tripInstance.purpose = ""
  131. tripInstance.notes = ""
  132. tripInstance.city = ""
  133. if (tripInstance.save(flush:true)) {
  134. container.addBean tripInstance
  135. } else {
  136. window.showInstance "Could not add trip"
  137. }
  138. }
  139. }
  140.  
  141. })
  142. toolbar.addComponent addButton
  143.  
  144. def removeButton = new Button("-", new Button.ClickListener() {
  145. void buttonClick(Button.ClickEvent event) {
  146. if (table.value) {
  147. Trip.withTransaction { status ->
  148. def tripInstance = table.value
  149. tripInstance.delete(flush:true)
  150. container.removeItem tripInstance
  151. table.value = null
  152. }
  153. }
  154. }
  155. })
  156. toolbar.addComponent(removeButton)
  157.  
  158. browser.addComponent toolbar
  159. browser.setExpandRatio table, 1.0f
  160.  
  161. mainWindow.addComponent browser
  162. mainWindow.addComponent tripEditor
  163. }
  164. }
  165. }}}
  166.  
  167. Most of the code is ordinary Vaadin UI creation code, which we will not discuss in this article. However, the interesting parts are those dealing with domain objects.
  168.  
  169. We start with the code that fetches the Trips from the database and puts them in a Vaadin container:
  170. {{{
  171. def container = new BeanItemContainer<Trip>(Trip.class)
  172. Trip.list().each {
  173. container.addBean it
  174. }
  175. }}}
  176.  
  177. This code snippet creates a standard Vaadin {{{BeanItemContainer}}}, fetches a list of all the Trips and adds each Trip to the container.
  178.  
  179. Next, we move on to the code for adding new Trips:
  180. {{{
  181. Trip.withTransaction { status ->
  182. def tripInstance = new Trip()
  183. tripInstance.name = "A new trip"
  184. tripInstance.endDate = new Date()
  185. tripInstance.startDate = new Date()
  186. tripInstance.purpose = ""
  187. tripInstance.notes = ""
  188. tripInstance.city = ""
  189. if (tripInstance.save(flush:true)) {
  190. container.addBean tripInstance
  191. } else {
  192. window.showInstance "Could not add trip"
  193. }
  194. }
  195. }}}
  196.  
  197. As Vaadin is not controlled by Grails, we have to handle transactions ourselves. Fortunately, each Grails domain class has a method named {{{withTransaction}}} that makes this easy (see the Grails [[http://grails.org/doc/latest/ref/Domain%20Classes/withTransaction.html|documentation]] for details).
  198.  
  199. While inside the transaction, we create a new {{{Trip}}} instance, populate it with default values (otherwise the database will not accept it), save it to the database and add it to the container so that it shows up in the UI. Please note, that a real-world application might want to ask the user for data before saving the domain object instead of using default data. Also, the validation errors could probably be handled in a more user friendly way.
  200.  
  201. Finally, we take a look at the code snippet for deleting Trips:
  202. {{{
  203. Trip.withTransaction { status ->
  204. def tripInstance = table.value
  205. tripInstance.delete(flush:true)
  206. container.removeItem tripInstance
  207. table.value = null
  208. }
  209. }}}
  210.  
  211. We fetch the currently selected Trip from the table, delete it from both the database and the container and clear the selection of the table. Again, we have to handle the transactions ourselves.
  212.  
  213. == Deploying the Vaadin UI ==
  214.  
  215. Now when the Vaadin UI has been implemented, we have to deploy it. We start by creating a new servlet in the {{{tripplanner}}} package (found in the //src/groovy// directory):
  216.  
  217. {{{
  218. package tripplanner
  219.  
  220. import javax.servlet.ServletException;
  221. import javax.servlet.http.HttpServletRequest;
  222.  
  223. import com.vaadin.Application;
  224.  
  225. class VaadinAppServlet extends com.vaadin.terminal.gwt.server.AbstractApplicationServlet {
  226. @Override
  227. protected Class<? extends Application> getApplicationClass()
  228. throws ClassNotFoundException {
  229. return VaadinApp.class
  230. }
  231.  
  232. @Override
  233. protected Application getNewApplication(HttpServletRequest request)
  234. throws ServletException {
  235. return new VaadinApp()
  236. }
  237. }
  238. }}}
  239.  
  240. We have to implement the class ourselves, as the default {{{ApplicationServlet}}} class cannot find the {{{VaadinApp}}} class for some reason.
  241.  
  242. Finally, we have to configure the //web.xml// file. As Grails normally uses a default file based on a template, we have to start by installing the template in order to be able to change it:
  243.  
  244. {{{$ grails install-templates}}}
  245.  
  246. Now open the //web.xml// file found in the //src/templates/war// directory and add the following elements:
  247.  
  248. {{{
  249. <servlet>
  250. <servlet-name>VaadinServlet</servlet-name>
  251. <servlet-class>tripplanner.VaadinAppServlet</servlet-class>
  252. </servlet>
  253.  
  254. <servlet-mapping>
  255. <servlet-name>VaadinServlet</servlet-name>
  256. <url-pattern>/VAADIN/*</url-pattern>
  257. </servlet-mapping>
  258. }}}
  259.  
  260. Now you can start the application again:
  261.  
  262. {{{$ grails run-app}}}
  263.  
  264. If everything went as expected, you should now be able to access the new Vaadin UI at [[http://localhost:8080/tripplanner/VAADIN]]. If you like, you can also open the UI generated by Grails and verify that the Trips show up in both user interfaces (please remember, though, that you have to restart the Vaadin application in order to refresh the list of Trips).
  265.  
  266. == Summary ==
  267.  
  268. Using Vaadin and Groovy is really no different than using Vaadin and Java, especially now when Groovy supports anonymous inner classes. Although I had never used Groovy before I wrote this article, its syntax seems to be more efficient than Java's, making it possible to do more with less lines of code. Therefore, I think Groovy and Vaadin may turn out to be a very nice combination.
  269.  
  270. As for Grails and Vaadin, I am not that convinced. //I had never used Grails before I wrote this article so I may be wrong//, but to me, it seems like Grails is a Groovy-based MVC framework with a built-in object persistence solution (built on top of Hibernate). As Vaadin and the Grails MVC are very different, the only reason why one would want to use Vaadin and Grails is the persistence solution.
  271.  
  272. So in conclusion, it is possible to write a Vaadin user interface for a Grails application. However, if you are starting a new project, you should ask yourself what you are winning by basing the application on Grails. If you can't answer that question, it might be a good idea to look at some alternatives (such as plain Groovy) before the coding begins.
  273.  
  274. **Attention all Grails experts out there:** If you can find a better way of incorporating Vaadin into grails, or better arguments for why it is a good idea to base Vaadin applications on Grails, please let me know!
  275.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement