プログラミング G* JJUG CCC 2011 Spring @kiy0taka
プログラミング G*
JJUG CCC 2011 Spring@kiy0taka
自己紹介•奥清隆(おく きよたか)
• id:kiy0taka、@kiy0taka
•大阪在住
•日本Grails/Groovyユーザグループ
•関西Javaエンジニアの会
※ kiy0takaの0は数字のゼロ
G*とは
•じーあすたりすく、じーあすたー
• GroovyやGroovyに関する技術の総称
• Grails、Griffon、Gant、Gaelyk等
•詳しくは全国各地のJGGUG勉強会へ
Groovy?
HelloWorld.java
public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, World!");
}}
HelloWorld.groovy
public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, World!");
}}
Javaの構文とある程度互換性があるのでそのまま使える。
HelloWorld.groovy
public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, World!")
}}
セミコロンはなくても良い。
HelloWorld.groovy
public class HelloWorld {public static void main(String[] args) {System.out.println "Hello, World!"
}}
引数ありメソッド呼び出しの括弧は省略可能
HelloWorld.groovy
public static void main(String[] args) {System.out.println "Hello, World!"
}
クラスも省略可能
HelloWorld.groovy
System.out.println "Hello, World!"
mainメソッドも省略可能
HelloWorld.groovy
println "Hello, World!"
System.out.println → println
One-Liner
groovy -e "println 'Hello, World!'"
Java資産の超有効活用
Javaのクラスを普通に使える
def file = new File("./hoge.txt")
拡張されてる
def text = new File("./hoge.txt").text
Webページをとってくる
new URL('http://www.java-users.jp/contents/').getText('UTF-8')
Stringでコマンド実行
println 'ls -la /opt/local'.execute().text
total 8drwxr-xr-x 12 root admin 408 11 23 19:32 .drwxr-xr-x 3 root admin 102 11 7 14:34 ..drwxr-xr-x 5 root wheel 170 11 24 13:02 Librarydrwxr-xr-x 686 root admin 23324 11 27 18:06 bindrwxr-xr-x 25 root admin 850 11 24 17:01 etcdrwxr-xr-x 177 root admin 6018 11 24 17:01 includedrwxr-xr-x 988 root admin 33592 11 24 17:01 libdrwxr-xr-x 22 root admin 748 11 24 16:17 libexeclrwxr-xr-x 1 65534 wheel 9 11 23 18:42 man -> share/mandrwxr-xr-x 5 root admin 170 11 24 13:37 sbindrwxr-xr-x 76 root admin 2584 11 27 18:06 sharedrwxr-xr-x 8 root admin 272 11 24 13:02 var
よく使われるパッケージのimport文は書かなくて良い
import java.langimport java.mathimport java.ioimport java.netimport java.utilimport groovy.langimport groovy.util
Javaのライブラリもそのまま使える
import org.apache.poi.hssf.usermodel.*
def workBook = new HSSFWorkbook(new File('./foo.xls')workBook.newInputStream()).sheets.each { sheet -> sheet.firstRowNum.upto(sheet.lastRowNum) { sheet.getRow(it).with { row -> row.firstCellNum.upto(row.lastCellNum - 1) { println row.getCell(it).stringCellValue } } }}
jarファイルがなくてもいい
@Grab('org.apache.poi:poi:3.2-FINAL')import org.apache.poi.hssf.usermodel.*
def workBook = new HSSFWorkbook(new File('./foo.xls')workBook.newInputStream()).sheets.each { sheet -> sheet.firstRowNum.upto(sheet.lastRowNum) { sheet.getRow(it).with { row -> row.firstCellNum.upto(row.lastCellNum - 1) { println row.getCell(it).stringCellValue } } }}
Antが組み込まれているdef ant = new AntBuilder()
ant.unzip(src: 'xxx.zip', dest:'dest')
ant.mail(mailhost:'hostname', subject:'hello', charset:'utf-8', user:user, password:password) { from address:'[email protected]' to address:'[email protected]' message 'Hello World!'}
効率よくプログラミングする
ために
• Groovyの起動が速くなる
• JVM上で動く言語は起動に時間がかかる
•テンポよくスクリプトを実装できる
•日本人が開発
•@uehaj、@nobeans
// Groovy$ time groovy -e "println 'Hello'"Hello
real!0m1.292suser!0m1.283ssys!0m0.192s
// GroovyServ$ time groovyclient -e "println 'Hello'"Hello
real!0m0.036suser!0m0.001ssys!0m0.003s
ツールを作る
Swing(Java)package sample;
import java.awt.Container;import java.awt.GridLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;
import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextArea;import javax.swing.SwingUtilities;
public class Hello extends JFrame {
public Hello() { super("Hello");
Container contentPane = getContentPane(); contentPane.setLayout(new GridLayout(3, 1));
JLabel label = new JLabel("Label"); contentPane.add(label);
JTextArea textArea = new JTextArea("Text Area"); textArea.setColumns(20); textArea.setRows(2); contentPane.add(textArea);
JButton button = new JButton("Button"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { ... } });
contentPane.add(button);
setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); setVisible(true); }
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new Hello(); } }); }}
Groovy
import groovy.swing.SwingBuilder
new SwingBuilder().edt { frame(title:'Hello', show:true, pack:true) { gridLayout(cols:1, rows:3) label 'Label' textArea('Text Area', rows:2, columns:20) button('Button', actionPerformed:{ evt -> ... }) }}
import groovy.swing.SwingBuilder
new SwingBuilder().edt { frame(show:true, pack:true) { tableLayout { tr { td { label 'UserName: ' } td { textField columns:20 } } tr { td { label 'Password: ' } td { passwordField columns:20 } } tr { td(colspan:2) { button 'Login' } } } }}
SwingBuilder
h2console.groovy
@Grab('org.mortbay.jetty:jetty-embedded:6.1.25')@Grab('com.h2database:h2:1.2.144')@Grab('mysql:mysql-connector-java:5.1.13')import org.mortbay.jetty.Serverimport org.mortbay.jetty.servlet.Contextimport org.h2.server.web.WebServlet
def server = new Server(8080)new Context(server, "/", Context.SESSIONS).addServlet(WebServlet, "/*")
server.start()
https://gist.github.com/717932
Groovy 1.8 のチカラ
please show the square_root of 100
Command Chainshow = { println it }square_root = { Math.sqrt(it) }
def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }]}
please(show).the(square_root).of(100)
show = { println it }square_root = { Math.sqrt(it) }
def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }]}
please show the square_root of 100
Command Chain
Object.metaClass.を =
Object.metaClass.の =
{ clos -> clos(delegate) }
まず = { it }表示する = { println it }平方根 = { Math.sqrt(it) }
まず 100 の 平方根 を 表示する
Command Chain
GPars
• Groovy Parallel Systems
• Groovyで並列プログラミング
• GParsはGroovyのライブラリ
• Groovy 1.8からGroovyに組み込まれた
処理を並列化
import static groovyx.gpars.GParsPool.withPool
def list = [1, 2, 3, 4, 5]
withPool { def result = list.collectParallel { it * 2 } assert result == [2, 4, 6, 8, 10]}
JSONのサポート
JsonSlurperimport groovy.json.*
def text = '''[ ["aaa", "bbb", "ccc"], { "key1" : "value1", "key2" : "value2", "key3" : "value3", "key4" : "" }, ["ddd", "eee", "fff"]]'''
def slurper = new JsonSlurper()def json = slurper.parseText(text)
assert json == [ ["aaa", "bbb", "ccc"], [ "key1" : "value1", "key2" : "value2", "key3" : "value3", "key4" : "" ], ["ddd", "eee", "fff"]]
JsonBuilderimport groovy.json.*
def json = new JsonBuilder()json ( ["aaa", "bbb", "ccc"], [ "key1" : "value1", "key2" : "value2", "key3" : "value3", "key4" : "" ], ["ddd", "eee", "fff"])
println json
クロージャの拡張
アノテーションで利用import org.gcontracts.annotations.*
@Invariant({ speed() >= 0 })class Rocket {
@Requires({ isStarted() }) @Ensures({ old.speed < speed }) def accelerate() { ... }
boolean isStarted() { ... } def speed() { ... }
}
合成def plus2 = { it + 2 }def times3 = { it * 3 }
def times3plus2 = plus2 << times3assert times3plus2(3) == 11assert times3plus2(4) == plus2(times3(4))
def plus2times3 = times3 << plus2assert plus2times3(3) == 15assert plus2times3(5) == times3(plus2(5))
assert times3plus2(3) == (times3 >> plus2)(3)
その他• トランポリン
• 再帰処理をループに
• メモ化
• 同じ引数の結果を再利用
• カリー化
• 引数の右からカリー化
• 指定した位置の引数をカリー化
新しいAST変換
@Logimport groovy.util.logging.*
@Logclass Car { Car() { log.info 'Car constructed' }}
def c = new Car()
@Commons、@Log4j、@Slf4j
@Log、@Commons、@Log4j、@Slf4j、@Field、@PackageScope、
@AutoClone、@AutoExternalizable、@ThreadInterrupt、@TimedInterrupt、@ConditionalInterrupt、@ToString、
@EqualsAndHashCode、@TupleConstructor、@Canonical、@InheritConstructors、@WithReadLock、@WithWriteLock、
@ListenerList
Webアプリケーションを作る
Grails
•フルスタックなWebアプリケーションフレームワーク
• Java、Groovy、Spring、Hibernate、Sitemeshなど
•データベース(HSQLDB)、サーブレットコンテナ(Tomcat)がバンドルされていてすぐ使える!
コマンドによるサポート
create-appcreate-controllercreate-domain-classcreate-scriptcreate-servicecreate-tag-libcreate-unit-test
generate-allgenerate-controllergenerate-viewspackagerun-apprun-wartest-app
create-app
create-domain-classpackage myapp
class Message {
String text
static constraints = { text blank:false, maxSize:500 }}
create-controller
package myapp
class MessageController {
static scaffold = true}
run-app
generate-all
•静的スカッフォールディング
•コントローラ、ビューのコードを生成
•テンプレートを適用することも可能
GORM• Grails O/R Mapping
•Message.list(offset:10, max:10)
•Message.findAllByTextLike('...')
•Message.withCriteria { like('text', '...') }
• new Message(text:'...').save()
install-plugin• 600以上のプラグイン
•既存のJavaテクノロジをプラグインで取り込む
• Spring Security、Mail、App Engine
• Cassandra、MongoDB、Redis、Riak、GemfireなどのNoSQLにも対応
package
•WARファイルにパッケージ
• Java Webアプリケーションと同じ
• Tomcatなど、通常のサーブレットコンテナで動かせる
Google App Enginefor Groovy
• GAE/J用のGroovyツールキット
• GrailsもGAE/J対応してるが、ちょっとしたアプリをサクっと作れる
Gaelyk:Controller
log.info "Setting attribute datetime"
request.datetime = new Date().toString()
log.info "Forwarding to the template"
forward '/datetime.gtpl'
Gaelyk:View
<% include '/WEB-INF/includes/header.gtpl' %>
<h1>Date / time</h1>
<p> <% log.info "outputing the datetime attribute" %> The current date and time: ${request.datetime}</p>
<% include '/WEB-INF/includes/footer.gtpl' %>
Gaelyk:Datastore
import com.google.appengine.api.datastore.Entity
def entity = new Entity("person")entity.name = "Kiyotaka Oku"entity.age = 31
entity.save()
datastore blobstore
memcache oauth
urlFetch namespace
mail capabilities
images channel
users files
user backends
defaultQueue lifecycle
queues localMode
xmpp app
import com.google.appengine.api.datastore.*import static com.google.appengine.api.datastore.FetchOptions.Builder.*import static com.google.appengine.api.datastore.Query.FilterOperator.*import static com.google.appengine.api.datastore.Query.SortDirection.*
def query = new Query('person').with { addFilter 'age', GREATER_THAN_OR_EQUAL, 30 addSort 'name', ASCENDING}
def persons = datastore.prepare(query).asList(withLimit(21))
Gaelyk:Datastore
Gaelyk:Mail
mail.send sender: "[email protected]", to: "[email protected]", subject: "Hello", textBody: "Hello, how are you doing?"
Gaelyk:TaskQueue
defaultQueue << [ countdownMillis: 1000, url: "/task/dailyEmail", taskName: "Send daily email newsletter", method: 'PUT', params: [date: '20090914'], payload: content]
デスクトップアプリケーション
を作る
Griffon
•デスクトップアプリケーションフレームワーク
• Grailsを元に開発された
Getting Started
$ griffon create-app myapp$ cd myapp$ griffon run-app
GriffonのMVC
• Model -> POGO
• View -> SwingBuilder
• Controller -> Groovy
MVCをきちんと分けて実装できる
プラグイン
• 様々な機能をプラグインで提供
• CSS、JavaFX、Test、Look&Feel...
• 100以上のプラグインを利用可能
• プラグインのインストールもコマンド一発
• griffon install <プラグイン名>
パッケージング
• 「griffon package」
• Jar、Zip、Applet、Web Start で配布可能
• Installerプラグインでさらに色んな形式で
• .exe、.app、IzPack、RPM、DEB、DMG
ビルドする
Gradle•ビルドツール
•Mavenのように定型化したタスク
• Ivy、Mavenの依存関係の解決
• Antのように独自タスクも定義可能
•ビルドスクリプトはGroovyで記述
• XMLからの解放
build.gradle
apply plugin: 'groovy'
repositories { mavenCentral()}
dependencies { groovy 'org.codehaus.groovy:groovy:1.8.0' compile 'junit:junit:4.8.2'}
$ gradle clean build:clean:compileJava:compileGroovy:processResources:classes:jar:assemble:compileTestJava:compileTestGroovy:processTestResources:testClasses:test:check:build
BUILD SUCCESSFUL
Total time: 13.766 secs
独自タスクの定義task checksum << { fileList('../antLoadfileResources').each {File file -> ant.checksum(file: file, property: "cs_$file.name") println "$file.name Checksum: ${ant.properties["cs_$file.name"]}" }}
task loadfile << { fileList('../antLoadfileResources').each {File file -> ant.loadfile(srcFile: file, property: file.name) println "I'm fond of $file.name" }}
File[] fileList(String dir) { file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()}
もっと知りたい方は
•日本Grails/Groovyユーザーグループ
•首都圏では2ヶ月に1回のペースで勉強会
•名古屋、大阪でも
G*Magazine
• JGGUGが発行する定期(?)発行電子書籍
• PDFで配布中
• Vol. 0、Vol. 1公開中!
• Vol. 2は今週末(?)
• http://grails.jp/g_mag_jp/
勉強会予定• 5/28(土)
•レッツゴーデベロッパー 2011
• 6/17(金)
•G*ワークショップ
• 7/2(土)
• Groovyイン・アクション読書会
ご清聴、ありがとうございました。