Memakai Fasilitas Structured Search & Replace Di IntelliJ IDEA

Setelah memakai sebuah program cukup lama, saya menemukan bahwa bila JOptionPane dipanggil di luar event dispatching thread (EDT), terkadang-kadang akan muncul kesalahan tak terduga secara acak. Walaupun kesalahan acak ini tidak akan menganggu jalannya aplikasi, kehadirannya bisa membuat pengguna menjadi tidak tenang. Oleh sebab itu, saya membuat sebuah wrapper yang akan memastikan bahwa JOptionPane dipanggil dari EDT.

Dengan demikian, saya perlu mengubah kode program seperti:

if (JOptionPane.showConfirmDialog(view.mainPanel, 'Apakah Anda yakin ingin menghapus?',
   'Konfirmasi Hapus', JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {
     return
}

menjadi berikut ini:

if (!DialogUtils.confirm(view.mainPanel, 'Apakah Anda yakin ingin menghapus?', 'Konfirmasi Hapus', 
   JOptionPane.WARNING_MESSAGE)) {
     return
}

Masalahnya adalah kode program yang harus diubah jumlahnya sangat banyak sekali. Saya tidak yakin dengan kualitas yang dihasilkan bila saya meng-edit kode program satu per satu di setiap class yang ada. Saya juga tidak bisa memakai fasilitas search and replace biasa karena parameter seperti pesan dan judul dialog bisa berbeda-beda.

Karena memakai IntelliJ IDEA, saya langsung teringat pada fasilitas structured replace yang dapat diakses melalui menu Edit, Find, Replace Structurally…. Structured search & replace dapat menyimpan bagian dari hasil pencarian sebagai variabel yang kemudian dapat dipakai sebagai replacement di posisi yang berbeda. Namun kelebihan utamanya yang sangat penting adalah pencarian dilakukan berdasarkan syntax Java atau Groovy sehingga whitespace (seperti spasi atau tab) dan urutan tidak akan mempengaruhi hasil pencarian.

Sebagai contoh, saya mengisi dialog yang muncul dengan template seperti pada gambar berikut ini:

Structured search & replace

Structured search & replace

Bagian yang diapit oleh tanda dollar ($) adalah nama variabel. Mereka memiliki fungsi yang hampir mirip dengan capturing group di regex. Bila saya men-klik tombol Edit variables…, saya bisa menyaring lebih lanjut lagi nilai variabel yang akan masuk dalam kategori pencarian:

Membatasi pencarian berdasarkan variabel

Membatasi pencarian berdasarkan variabel

Bila saya memulai pencarian, saya akan menemukan hasil seperti berikut ini:

Hasil pencarian

Hasil pencarian

Untuk melihat seperti apa hasil perubahan yang akan dilakukan, saya dapat men-klik tombol Preview Replacement. Selain itu, saya juga bisa mencoba mengubah hanya 1 baris terlebih dahulu dengan men-klik tombol Replace Selected. Setelah yakin dengan hasil perubahan, saya pun segera men-klik tombol Replace All untuk melakukan perubahan secara global pada 53 struktur kode program yang ditemukan tersebut.

Sebagai contoh lain, saya juga ingin semua kode program untuk closure close diapit oleh execInsideUISync sehingga kode program apa pun yang ada di dalamnya akan dikerjakan di EDT. Untuk mencapai tujuan tersebut, saya dapat menggunakan template seperti berikut ini:

Structured search & replace

Structured search & replace

Saya menghilangkan def di bagian replacement template agar IntelliJ IDEA tidak menghasilkan def yang duplikat. Ini adalah perilaku yang aneh karena IDEA harusnya bisa lebih pintar. Walaupun demikian, yang terpenting adalah pada akhirnya 29 method berhasil dimodifikasi secara global oleh structured search dengan hasil sesuai yang diharapkan.

Iklan

Belajar Memakai Inspections Di IntelliJ IDEA

Salah satu fasilitas unik yang ada di IntelliJ IDEA adalah apa yang disebut sebagai inspections.  Ini mirip seperti fitur yang ditawarkan oleh FindBugs (http://findbugs.sourceforge.net) tetapi terintegrasi langsung pada IDE dan lebih lengkap lagi.  Pada versi gratis-nya, IntelliJ IDEA Community Edition, hanya inspections untuk Java dan Groovy yang aktif.

Inspections akan memberikan saran dan peringatan bila ada kode program yang mencurigakan.  Kode program memang benar secara struktur bahasa (tidak memiliki syntax error), akan tetapi kode program bisa saja sulit dimengerti atau memiliki bug.  Apa saja pemeriksaan yang bisa dilakukan oleh inspections di IntelliJ IDEA?  Saya dapat melihatnya dengan memilih menu File, Settings…  Pada dialog yang muncul, saya kemudian memilih menu Editor, Inspections.  Disini, saya bisa melihat apa saja pemeriksaan yang bisa dilakukan beserta keterangannya di bagian description seperti yang terlihat pada gambar berikut ini:

Memilih jenis inspections yang akan dilakukan.

Memilih jenis inspections yang akan dilakukan.

Pada komputer yang lambat, inspections kerap membuat ‘dunia’ menjadi lambat.  Untuk memakai IDEA sebagai editor biasa dan mengurangi kepintarannya, saya dapat men-klik tombol penghapus untuk mematikan seluruh inspections yang ada:

Mematikan seluruh inspections.

Mematikan seluruh inspections.

Secara default, tidak seluruh inspections aktif.  Apakah bila saya mengaktifkan seluruhnya berarti saya bisa menghasilkan kode program yang paling berkualitas?  Tidak!  Tidak ada satu pendekatan universal mengenai kualitas kode program.  Masing-masing developer juga bisa memiliki style-nya masing-masing.  Saya hanya perlu memilih beberapa aturan penting dan menerapkannya secara konsisten!  Yup, ‘konsisten’ adalah salah satu rahasia umum untuk menjaga kualitas kode program!

Saya perlu memilih secara seksama jenis pemeriksaan yang ingin saya lakukan.  Alasan lain saya tidak mengaktifkan seluruh pemeriksaan adalah beberapa pemeriksaan sesungguhnya saling bertolak belakang.  Sebagai contoh, perhatikan 2 jenis pemeriksaan berikut ini:

Contoh inspection yang saling bertolak belakang.

Contoh inspection yang saling bertolak belakang.

Pemeriksaan pertama, Instance method call not qualified with ‘this’, akan memberikan peringatan bila akses ke method di sebuah class tidak diawali dengan this.  Ini akan membuat kode program menjadi seperti pada bahasa pemograman PHP yang menggunakan this untuk mengakses method lain di dalam class yang sama. Sebaliknya, pemeriksan Unnecessary 'this' qualifier akan memberikan peringatan bila akses ke method di dalam class yang sama diawali dengan this. Hal ini karena code style dengan mengawali menggunakan this sering dianggap membingungkan. Dengan demikian, saya tidak dapat mengaktifkan kedua pemeriksaan tersebut secara bersamaan! Saya perlu memilih salah satu atau tidak mengaktifkan keduanya 🙂

Setelah mengaktifkan inspections, saya akan menemukan sebuah kotak kecil di sisi kanan editor, seperti yang terlihat pada gambar berikut ini:

Hasil inspection langsung diperoleh saat program diketik.

Hasil inspection langsung diperoleh saat program diketik.

Kotak ini akan berisi warna sesuai dengan hasil pemeriksaan. Secara default, warna kuning untuk Warning, warna merah untuk Error, warna hijau terang untuk Typo, dan sebagainya. Warna ini juga bisa diatur oleh pengguna.

Bila saya meletakkan pointer mouse agak lama di kotak kuning tersebut, saya akan menemukan informasi seperti:

Informasi hasil inspections.

Informasi hasil inspections.

Warning bukan sebuah kesalahan kode program (dilihat dari syntax atau struktur bahasa), tetapi sesuatu yang mencurigakan. Program tetap dapat berjalan walaupun ada kesalahan pemeriksaan dengan tingkat warning.

Untuk memperbaiki kesalahan, saya dapat menekan tombol Alt + Enter pada bagian kode program yang bermasalah. Saran perubahan akan muncul secara otomatis. Selain itu, bila saya menekan tombol panah kanan, saya bisa memilih untuk mematikan pemeriksaan secara global atau mengabaikan pemeriksaan untuk baris tersebut, seperti yang terlihat pada gambar berikut ini:

Aksi yang dapat dilakukan terhadap hasil inspection.

Aksi yang dapat dilakukan terhadap hasil inspection.

Selain pemeriksaan yang dilakukan secara otomatis setiap kali saya mengetik kode program, saya juga bisa menjalankan inspections secara global dengan memilih menu Analyze, Inspect Code. Pada dialog yang muncul, saya bisa memilih jenis pemeriksaan dan scope pemeriksaan. Setelah men-klik tombol Ok, saya akan memperoleh hasil yang terlihat seperti pada gambar berikut ini:

Hasil inspections secara global.

Hasil inspections secara global.

Salah satu fasilitas baru dari IntelliJ IDEA 14 adalah menu Analyze, Code Cleanup…. Fitur ini akan melakukan pemeriksaan secara global (sesuai dengan scope yang telah ditentukan) dan melakukan perbaikan secara otomatis bila memungkinkan.

Bila saya ingin mematikan inspections untuk sebuah file, saya dapat men-klik tombol wajah yang disebut Hector yang terletak di bagian kanan bawah editor, seperti yang terlihat pada gambar berikut ini:

Mematikan inspections pada file tertentu.

Mematikan inspections pada file tertentu.

Pada popup yang muncul, saya dapat menggeser slider ke posisi Syntax sehingga hanya pemeriksaan tata bahasa saja yang dilakukan atau ke posisi None untuk tidak melakukan pemeriksaan sama sekali. Selain itu, saya dapat memberi tanda centang pada Power Save Mode untuk membuat IntelliJ IDEA bekerja lebih cepat dengan mengorbankan kepintarannya.

Belajar Memakai Stashing Di Git

Salah satu fitur menarik di Git adalah stashing.   Dengan melakukan stashing, developer dapat dengan mudah ‘melenyapkan‘ perubahan kode program, melakukan perubahan lain, lalu kembali lagi ke kode program yang sebelumnya dikerjakan.   Langkah-langkah tersebut sebenarnya dapat diwakili oleh beberapa perintah Git lainnya, tapi stashing membuatnya menjadi mudah karena hanya perlu memanggil satu perintah.

Sebagai contoh, saya akan menggunakan Git yang diakses melalui IntelliJ IDEA.   Saya membuat sebuah proyek Groovy baru dengan nama latihan.   Lalu saya membuat sebuah script Groovy sederhana bernama Latihan.groovy untuk menghitung sisa inventory secara FIFO, seperti berikut ini:

List  pembelian = [10, 20, 30, 40]
List penjualan = [5, 6, 3, 2, 1, 3]
println stokFifo(pembelian, hitungTotal(pembelian, penjualan))

int hitungTotal(List pembelian, List penjualan) {
    pembelian.sum() - penjualan.sum()
}

List stokFifo(List pembelian, int sisa) {
    List hasil = []
    pembelian.each { int jumlah ->
        if (sisa > 0) {
            int delta = (jumlah >= sisa)? jumlah: sisa  // BUG YANG DISENGAJA!
            sisa -= delta
            hasil << delta
        }
    }
    hasil
}

Untuk menambahkan proyek ke dalam repository Git, saya memlih menu VCS, Import into Version Control, Create Git Repository… seperti yang terlihat pada gambar berikut ini:

Membuat Repository Baru

Membuat Repository Baru

Pada kotak dialog yang muncul, saya memilih untuk meletakkan repository bersamaan dengan lokasi proyek dan kemudian men-klik tombol OK.

Kemudian, saya men-klik kanan pada file Latihan.groovy, memilih menu Git, Add seperti yang terlihat pada gambar berikut ini:

Menambahkan file ke index

Menambahkan file ke index

Sekarang file tersebut telah berada di lokasi index atau staging dari Git.   Berikutnya, saya akan men-commit perubahan.   Caranya adalah dengan memilih menu VCS, Commit Changes….  Akan muncul kotak dialog seperti yang terlihat pada gambar berikut ini:

Men-commit perubahan

Men-commit perubahan

Saya memilih tombol Commit untuk menyimpan perubahan dalam repository Git lokal.   Saat IntelliJ IDEA memunculkan dialog mengenai file lain yang tidak ikut di-commit, saya memilih No.

Anggap saja ini adalah sebuah aplikasi yang dikembangkan bersama dengan developer lain, yaitu Lena.   Gadis manis tersebut telah men-fetch repository saya dari remote/upstream, kemudian ia akan men-develop kode program yang berhubungan dengan kode program saya di atas.

Tentu saja saya juga tidak ingin ketinggalan sibuk.   Saya menambahkan harga dan perhitungan laba pada kode program saya.  Sambil mendengarkan lagu klasik, saya menulis kode program berikut ini:

Map pembelian = [10: 10000, 20: 10100, 30: 10200, 40: 11000]
Map penjualan = [5: 12000, 6: 13000, 3: 11000, 2: 11000, 1: 15000, 3: 12000]
println stokFifo(pembelian, hitungTotal(pembelian.keySet().toList(), penjualan.keySet().toList()))

int hitungTotal(List pembelian, List penjualan) {
    pembelian.sum() - penjualan.sum()
}

List stokFifo(Map pembelian, int sisa) {
    List hasil = []
    pembelian.each { jumlah, hargaBeli ->
        if (sisa > 0) {
            int delta = (jumlah >= sisa)? jumlah: sisa  // BUG YANG DISENGAJA!
            sisa -= delta
            hasil << [delta, hargaBeli]
        }
    }
    hasil
}

int hitungProfit(List stokFifo, Map penjualan) {
    def

Saya belum selesai mengetik, bahkan belum sempat menjalankan kode program ketika tiba-tiba telepon berdering.   Suara Lena terdengar sangat panik.   Dia mengatakan bahwa 30 menit lagi dirinya harus memberikan presentasi ke pengguna, tapi ada yang aneh dengan perhitungan inventory buatan saya!   Lena menemukan sebuah kesalahan.  Lebih dari itu, Lena meminta saya untuk segera memperbaikinya secepat mungkin sehingga dia bisa men-pull perbaikan dari saya!

Masalahnya: saya sudah mengubah banyak kode program tersebut sehingga tidak sama lagi seperti yang dipakai oleh Lena.   Saya tidak ingin menghapus perubahan yang sudah saya buat sejauh ini karena nantinya perubahan ini pasti akan dipakai.

Salah satu solusi yang dapat saya pakai adalah dengan memakai fasilitas stashing di Git.   Saya memilih menu VCS, Git, Stash Changes…. Pada kotak dialog yang muncul, saya mengisi seperti berikut ini:

Men-push stash baru

Men-push stash baru

Setelah men-klik tombol Create Stash, isi kode program saya secara ajaib kembali lagi seperti semula!  Sama persis seperti commit terakhir yang dirujuk oleh HEAD.   Saya segera memanfaatkan kesempatan ini untuk mencari dan memperbaiki kesalahan yang ditemukan Lena:

List  pembelian = [10, 20, 30, 40]
List penjualan = [5, 6, 3, 2, 1, 3]
println stokFifo(pembelian, hitungTotal(pembelian, penjualan))

int hitungTotal(List pembelian, List penjualan) {
    pembelian.sum() - penjualan.sum()
}

List stokFifo(List pembelian, int sisa) {
    List hasil = []
    pembelian.each { int jumlah ->
        if (sisa > 0) {
            int delta = (jumlah >= sisa)? sisa: jumlah
            sisa -= delta
            hasil << delta
        }
    }
    hasil
}

Saya segera men-commit perubahan dengan memilih menu VCS, Commit Changes….   Pada komentar, saya mengisi dengan ‘Perbaikan perhitungan sisa inventory yang salah.’ dan men-klik tombol Commit.   Saya kemudian men-push perubahan ke upstream, sehingga Lena bisa men-pull perubahan dari saya.   Tidak lama kemudian suara Lena terdengar lega seolah-olah baru saja luput dari malapetaka.  Ia memberitahukan bahwa kini semuanya baik-baik saja.

Lalu bagaimana dengan kode program yang sedang saya ‘ketik‘ sebelum menerima telepon dari Lena?  Kemana perginya?   Saya bisa mengembalikannya dengan memilih menu VCS, Git, UnStash Changes….   Pada kotak dialog yang muncul, saya men-klik tombol Pop Stash seperti yang terlihat pada gambar berikut ini:

Men-pop dari Stash

Men-pop stash

Kode program saya akan kembali seperti terakhir kali:

Map pembelian = [10: 10000, 20: 10100, 30: 10200, 40: 11000]
Map penjualan = [5: 12000, 6: 13000, 3: 11000, 2: 11000, 1: 15000, 3: 12000]
println stokFifo(pembelian, hitungTotal(pembelian.keySet().toList(), penjualan.keySet().toList()))

int hitungTotal(List pembelian, List penjualan) {
    pembelian.sum() - penjualan.sum()
}

List stokFifo(Map pembelian, int sisa) {
    List hasil = []
    pembelian.each { jumlah, hargaBeli ->
        if (sisa > 0) {
            int delta = (jumlah >= sisa)? sisa: jumlah
            sisa -= delta
            hasil << [delta, hargaBeli]
        }
    }
    hasil
}

int hitungProfit(List stokFifo, Map penjualan) {
    def

Tunggu dulu!  Tidak persis sama seperti terakhir kali!!  Fasilitas stashing bukan saja hanya mengembalikan kode program sebelumnya, tetapi juga melakukan merging dengan perubahan saat ini.   Perbaikan yang diminta oleh Lena tidak hilang setelah saya men-pop stash.

Pada contoh ini, hanya terdapat satu file yang berubah.   Fasilitas stashing akan semakin berguna bila perubahan telah dilakukan pada banyak file yang berbeda.  Tanpa Git dan stashing, pada kasus ini, saya harus memakai cara manual yang lebih repot seperti memberikan komentar atau mengedit file untuk sementara.

An intro to simple-jpa: a bookstore tutorial

simple-jpa is a Griffon’s plugin for developing JPA and Swing based desktop application. The main goal of simple-jpa is to allow developer to concentrate on business logic. simple-jpa provides much functionality that is needed when working with JPA, therefore, frees developer from writing high-ceremony code.

simple-jpa is very useful for rapidly developing Swing-based database oriented desktop application. It can also be used for prototyping.

The following is a list of some of simple-jpa’s features:

Scaffolding – simple-jpa can generate an MVCGroup based on a domain class. This will speed up development.

Dynamic finders – simple-jpa injects dynamic finders to controllers (or services). With dynamic finders, developer can perform a query on JPA entities (or domain objects) quickly and easily. simple-jpa also supports the execution of JPA named query, JPQL and native SQL.

Transaction management – Unlike web-based applications, desktop applications do not require Java Transaction API (JTA). simple-jpa automatically provides and manages transaction for each method in controllers (can be configured by using annotation). By default, simple-jpa will share EntityManager across transaction in a way that is suitable for desktop application.

Bean Validation API (JSR-303) support – In the case of failed validation, simple-jpa will automatically present error messages in Swing-based view. Developer can also configure error notification and its behavior.

Common database application features – simple-jpa adds the following to all domain classes: an id (auto generated primary key), fields that store created time and last modified time (will be filled automatically), and a soft delete flag (soft delete is marking the object as inactive without deleting it from database).

Swing nodes for database application – simple-jpa provides template renderer for effortlessly represent domain object in JTable, JList or JComboBox. It also provides new nodes that can be used in Griffon’s view such as tagChooser, numberTextField, maskTextField, and dateTimePicker.

Integration testing – simple-jpa is using dbUnit in integration testing to fill database with predefined data from a Microsoft Excel file (or csv file). This way, every test cases will be executed with the same table data.

This tutorial will assume that you are using the following software:

  1. Windows operating system
  2. Java Development Kit 7
  3. Griffon 1.2
  4. simple-jpa 0.4
  5. IntelliJ IDEA
  6. MySQL Server

This tutorial will assume that you are using the following software:

  1. Windows operating system
  2. Java Development Kit 7
  3. Griffon 1.2
  4. simple-jpa 0.4
  5. IntelliJ IDEA
  6. MySQL Server

The first step is creating a new Griffon application. You can do this by opening Command Prompt and entering the following commands:

mkdir c:\projects
cd c:\projects
griffon create-app bookstore

You can change c:\projects in the above commands to another directory. If that directory already exists, you can skip the ‘mkdir` command.

Griffon will create a subdirectory called bookstore that contains your project’s files, including source code. You will need to move to this directory by entering the following command:

cd bookstore

Next, you will install simple-jpa plugin by entering the following command:

griffon install-plugin simple-jpa 0.4

If simple-jpa has never been installed on your computer, Griffon will download required files for the first time. simple-jpa is a fat plugin with a lot of dependencies, so you may need to wait.

After simple-jpa has been downloaded and installed, the next step is integrating your project with IntelliJ IDEA by entering the following command:

griffon integrate-with -–idea

You can also replace –idea with –eclipse if you’re using Eclipse. In the above command, Griffon will create an IntelliJ IDEA project in your current directory. You can also create a new Griffon’s project in IntelliJ IDEA from the beginning, without using any command line as in this tutorial, but there is a drawback. If you create project and install simple-jpa from inside IntelliJ IDEA, dependencies for current project will not setup properly and autocomplete for project dependencies will not work.

You can start IntelliJ IDEA now. With IntelliJ IDEA running, select Open Project, browse to C:\projects\bookstore, and then click on OK button to open your project.

Opening Griffon's project in IntelliJ IDEA

Opening Griffon’s project in IntelliJ IDEA

JPA application development usually starts with creating persistence.xml. This file will contain information required for database connection and JPA configurations. It will also contain a list of entities that will be managed by JPA.

The next most common thing to start with is creating database user and schema.

To keep things simple, you will use create-simple-jpa command. This command will generate persistence.xml for you. If you want it to also generate database schema and a new user, you will need to provide MySQL’s root password. Note that MySQL’s root password will not be saved and will not be used inside application.

In this step, you will create persistence.xml, a new database named bookstore and a database user steven (whose password is 12345). You will need to select Tools, Griffon, Run Target (or press Ctrl+Alt+G) and enter the following command:

create-simple-jpa --user=steven --password=12345 --database=bookstore --rootPassword=adminpassword
Entering Griffon's command in IntelliJ IDEA

Entering Griffon’s command in IntelliJ IDEA

The command above is assuming that your root password is adminpassword. In some cases, your root password may be empty, so you will need to use the following command instead:

create-simple-jpa --user=steven --password=12345 --database=bookstore --rootPassword=

If you only want to generate persistence.xml without performing any database operations, you can use the following command instead:

create-simple-jpa --user=steven --password=12345 --database=bookstore --skipDatabase=true

The next step will be creating domain classes. Your bookstore application will have the following domain classes: TransactionOrder, OrderItem, Book, and Author. To create domain classes and add their location to persistence.xml, you will need to enter the following command:

create-domain-class TransactionOrder OrderItem Book Author

You will find all generated domain classes in the domain package. They are plain JPA entities that follow JPA rules.

simple-jpa domain classes

simple-jpa domain classes

Open TransactionOrder.groovy and add some members to this domain class:

package domain
import …

@DomainModel @Entity @Canonical
class TransactionOrder {

   @NotEmpty @Size(min=5, max=5)
   String orderNumber

   @Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
   LocalDate orderDate

   @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true , mappedBy="transactionOrder")
   List<OrderItem> orderItemList = []

   BigDecimal getAmount() {
      orderItemList.sum { it.amount }
   }

}

P.S.: If you didn’t add cascade and orphanRemoval to @OneToMany annotation, the code generated by scaffolding will not work. You’re also expected to initialize the Collection.

Open OrderItem.groovy and add some members to this domain class:

package domain

import ...

@DomainModel @Entity @Canonical (excludes = "transactionOrder")
class OrderItem {

   @NotNull @ManyToOne
   Book book

   @NotNull @Min(1l)
   Integer quantity

   @ManyToOne
   TransactionOrder transactionOrder

   BigDecimal getAmount() {
      book.price * quantity
   }

}

Open Author.groovy and add some members to this domain class:

package domain

import ...

@DomainModel @Entity @Canonical (excludes = "bookList")
class Author {

   @NotEmpty @Size(min=2, max=100)
   String firstName

   @Size(min=2, max=100)
   String lastName

   @Email
   String email

   @ManyToMany(mappedBy="authorList")
   List<Book> bookList

}

Open Book.groovy and add some members to this domain class:

package domain

import ...

@DomainModel @Entity @Canonical
class Book {

   @NotEmpty @Size(min=9, max=13)
   String isbn

   @NotEmpty @Size(min=2, max=100)
   String title

   @NotNull @Min(1l)
   BigDecimal price

   @NotEmpty @ManyToMany
   List<Author> authorList

}

Your next step will be generating Griffon’s MVCGroups based on your domain classes. To do this, enter the following command:

generate-all * --startupGroup=MainGroup

simple-jpa will create one MVCGroup per domain class. Each MVCGroup has model, view and controller that are capable of performing create, read, update and delete operation.

The above command will also generate one special MVCGroup named MainGroup which is a startup group. This MVCGroup will act as a main window that provides button to launch other MVCGroups.

By default, generate-all will not replace existing files. If you really want to overwrite existing files, you can use the following command instead:

generate-all * --startupGroup=MainGroup --force-overwrite

Up to this point, you already have a working bookstore application. You don’t need to worry about creating database tables because simple-jpa has added hibernate.hbm2ddl.auto to persistence.xml. This configuration will instruct Hibernate JPA to automatically create tables for domain classes in application startup.

You can start your application by entering the following command:

run-app

The startup group’s view contains buttons that link to the others.

simple-jpa startup group

simple-jpa startup group

P.S.: Every time you start your application, data in tables will be discarded. You will need to remove hibernate.hbm2dll.auto in persistence.xml if you want to preserve data in your tables.

One of simple-jpa’s nice features is that it knows about your domain classes’ relationship. simple-jpa will generate a special MVCGroup if it finds one-to-one and one-to-many relationship. For example, if you open Transaction Order view, you will find an Order Item List button. This is an example of one-to-many relationship. If you click on the button, a dialog to input one or more OrderItem will be displayed.

scaffolding one-to-many relationship

scaffolding one-to-many relationship

If you have examined the startup group clearly, you will find that there is an Order Item menu in your startup group. This is usually undesirable, because, user will always open Order Item view from Transaction Order view. You can remove it by opening MainGroupView.groovy, deleting line 12 and 27 as shown below:

actions {
   …
   action(id: 'orderItem', name: 'Order Item', actionCommandKey: 'orderItem', closure: controller.switchPage)
    …
}

toolBar(constraints: BorderLayout.PAGE_START, floatable: false) {
    …
    button(action: orderItem, verticalTextPosition: SwingConstants.BOTTOM, horizontalTextPosition: SwingConstants.CENTER)
    …
}

Your bookstore application will perform validation based on the annotations in domain classes. For example, firstName in Author class has @NotEmpty annotation. If user didn’t input author’s first name when entering new data, your application will display an error message.

simple-jpa validation

simple-jpa validation

If user input a new Book, he will need to choose authors (one or more) from simple-jpa’s tagChooser. By default, simple-jpa will use tagChooser if it encounters many-to-many relationship. tagChooser will display String version of domain object which is obtained by calling toString(). This is often seen as undesirable.

simple-jpa tagChooser

simple-jpa tagChooser

You can use template renderer to represent domain object in more informative text. For example, open BookView.groovy and change line 53 to the following:

tagChooser(model: model.authorList, templateString: '${value.firstName} - ${value.lastName}',
   constraints: 'grow,push,span,wrap', errorPath: 'authorList')

In the code above, you’re using simple-jpa’s template renderer to display Author’s first name and last name in tagChooser. Template renderer can accept any valid Groovy expression.

simple-jpa template renderer

simple-jpa template renderer

You can also use template renderer in table. For example, in the picture above, the fourth column (author list) is hard to read. Open BookView.groovy and change line 36 to the following:

columnValues: ['${value.isbn}', '${value.title}', '${value.price}',
    '<% out << value.authorList.collect{ "${it.firstName} - ${it.lastName}"}.join(", ") %>'])

The expression above will display a comma separated list of authors for each book.

Using simple-jpa template renderer in table

Using simple-jpa template renderer in table

If you are displaying number or money amount in template renderer, you can also use simple-jpa’s formatter functions such as numberFormat() or currencyFormat(). For example, open TransactionOrderView.groovy and change line 36 to the following:

columnValues: ['${value.orderNumber}', '${value.orderDate}',
    '${value.orderItemList.size()} items, Total ${currencyFormat(value.amount)}'])
Using formatter in template renderer

Using formatter in template renderer

Memakai Griffon Di IntelliJ IDEA

Selama ini, saya memakai STS  yang berbasis Eclipse untuk bermain-main dengan Griffon.  STS memang mendukung Groovy, tapi tidak ada plugin terintegrasi di STS untuk Griffon.  Saya membutuhkan sesuatu yang lebih nyaman.  Setelah melakukan pencarian singkat di Google, saya menemukan bahwa IDE ‘terbaik’ yang mendukung Groovy dan Griffon adalah IntelliJ IDEA.  Beberapa bagian source code IntelliJ IDEA bahkan dibuat dengan Groovy.

Saat melihat logo IntelliJ IDEA, saya langsung merasakan suasana nostalgia.  Maklum saja, pada saat saya pertama kali bekerja di industri software sebagai developer Java, saya memakai IntelliJ IDEA.  Saya merasa sangat nyaman dengan IDE yang satu ini.  Sayangnya, IntelliJ IDEA tidak gratis!  Begitu pindah ke lokasi kerja yang baru, saya kehilangan lisensi dan akhirnya beralih ke Eclipse yang gratis!  Well, saya merasa sangat nyaman dengan Eclipse hingga sekarang (walaupun pada saat mengajar, saya memakai NetBeans karena mahasiswa lebih nyaman dengan NetBeans).

Tapi IntelliJ IDEA kini memiliki versi open source yang dapat di-download gratis, yaitu IntelliJ IDEA Community Edition.  Saya dapat men-download versi terbarunya di http://www.jetbrains.com/idea/download/.

Setelah IntelliJ IDEA di-install, pada saat membuat proyek baru, sudah ada pilihan untuk membuat aplikasi Groovy dan Griffon, seperti yang diperlihatkan pada gambar berikut ini:

Membuat Proyek Griffon Baru

Membuat Proyek Griffon Baru

Karena masih baru, pilihan di Project SDK masih <None>.  Oleh sebab itu, saya men-klik tombol New… dan memilih JDK.  Setelah itu, saya memilih lokasi dimana saya men-install Java SDK.

Setelah itu, saya men-klik tombol Next.  Pada halaman ini, saya akan mendapatkan pesan Error: library is not specified karena ini adalah pertama kalinya saya menjalankan IntelliJ IDEA.   Saya perlu men-klik tombol Create,  kemudian memilih lokasi dimana saya men-install Griffon.  Tampilan akan menjadi terlihat seperti pada gambar berikut ini:

Memilih Griffon SDK

Memilih Griffon SDK

Setelah itu, saya men-klik tombol Finish untuk menyelesaikan proses pembuatan proyek baru.

Sebagai perbandingan, pada saat memakai Eclipse (STS), saya tidak bisa membuat proyek baru Griffon dari dalam IDE.  Saya harus menggunakan Command Prompt untuk menghasilkan proyek, kemudian men-import proyek tersebut di Eclipse.  IntelliJ IDEA lebih memanjakan saya, karena setelah proyek baru dibuat, akan muncul dialog yang berisi perintah yang biasanya saya ketik untuk membuat proyek Griffon baru, seperti yang terlihat pada gambar berikut ini:

Dialog Create Griffon Structure

Dialog Create Griffon Structure

Saya memilih pilihan pertama (create-app) dan men-klik tombol OK.

Tampilan project akan terlihat seperti pada gambar berikut ini:

Tampilan Proyek

Tampilan Proyek

Tapi, saya bisa berpindah ke Griffon View untuk resources berdasarkan pengkategorian secara logika (bukan lokasi fisik), seperti yang terlihat pada gambar berikut ini:

Tampilan Griffon View

Tampilan Griffon View

Fitur seperti ini tidak ada di Eclipse (STS) karena hingga saat ini memang belum ada plugin Griffon untuk Eclipse.   Selain itu, disini saya bisa langsung menjalankan proyek dengan tombol Shift+F10 tanpa melalui Ant ataupun eclipse.launch, seperti yang diperlihatkan oleh gambar berikut ini:

Menjalankan Proyek

Menjalankan Proyek

Melakukan debugging kode proram Griffon di Eclipse adalah salah satu hal yang cukup melelahkan.  Beruntungnya, pada IntelliJ IDEA  saya tinggal memberikan breakpoint dan men-klik tombol Debug (Shift+F9).   Secara teori, bila developer sudah membuat unit test secara detail untuk setiap class yang ada, maka tidak perlu ia tidak perlu melakukan debugging lagi.   Tapi bagi saya, fitur debugging terkadang masih tetap dibutuhkan untuk mencari kesalahan, terutama pada bahasa dinamis seperti Groovy.

Bicara soal unit test, saya bisa menjalankan kode program pengujian yang dihasilkan oleh Griffon secara langsung di dalam IntelliJ IDEA dengan men-klik kanan pada folder Unit Tests dan memilih Run ‘All Tests’ (Ctrl+Shift+F10).  Tampilan hasil pengujian akan terlihat seperti pada gambar berikut ini:

Tampilan Hasil Pengujian

Tampilan Hasil Pengujian

Bagaimana bila saya ingin menjalankan salah satu perintah console Griffon?  Apakah saya perlu membuka Command Prompt seperti saat memakai Griffon di STS (Eclipse)?   Tida perlu!  Di IntelliJ IDEA, saya cukup memilih Tools, Griffon, Run Target (Ctrl+Alt+G) seperti yang terlihat pada gambar berikut ini:

Menjalankan Perintah Griffon

Menjalankan Perintah Griffon

Output dari perintah yang saya berikan akan tampil di view Griffon Console.