Monday, August 04, 2008

code ของ grails 's plugin

คราวก่อนที่เจอปัญหา xml-rpc บน grails, ผมได้นั่งแกะโครงสร้าง plugins ของ grails ไปแวบหนึ่ง
รู้สึกติดใจว่า ถ้ามีเวลาต้องมานั่งแกะให้เป็นเรื่องเป็นราว

วันนี้ได้ที ใน mailing list ของ th-grails-user มีปัญหาซึ่งพาดพิงถึง mail-plugin
ก็เลยจัดการ install แล้วก็นั่งแกะ code ว่ามันทำอะไรบ้าง

function หลักๆที่ mail plugin ทำก็คือ
  • สร้าง service ที่ชื่อ mailService เพื่อให้ controller หรือ domain เรียกใช้ตามอัธยาศัย
    โดย service นี้ จะทำหน้าที่สร้าง builder ที่จะใช้แปลง closure ที่ pass เข้ามา ให้กลายเป็น mail message
    ก่อนจะ delegate ต่อให้ mailSender bean เป็นคนจัดการส่งออกไป
    (mailSender แอบใช้ของที่ spring มีอยู่แล้ว นั่นคือ org.springframework.mail.javamail.JavaMailSenderImpl)
  • สร้าง method ที่ชื่อ sendMail ให้กับ controller ทุกตัวที่มี ผ่านทางกลไกของ metdata


code ในส่วนของ function แรกที่น่าสนใจ ก็คือการ define bean 'mailSender'
ลองดู code ที่มันใช้
def doWithSpring = {
def config = application.config.grails.mail
mailSender(JavaMailSenderImpl) {
host = config.host ?: "localhost"
defaultEncoding = config.encoding ?: "utf-8"
if(config.port)
port = config.port
if(config.username)
username = config.username
if(config.password)
password = config.password
if(config.protocol)
protocol = config.protocol
if(config.props instanceof Map && config.props)
javaMailProperties = config.props
}
}

method นี้จะถูกเรียกใช้ในช่วง startup ของ grails
ซึ่งข้างในจะเห็นว่ามันใช้ DSL ในการ config spring
code ข้างบน ถ้าเขียนในแบบดั้งเดิมของ spring ก็จะออกมาทำนองนี้
<bean id="mailSender" 
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<host>localhost</host>
<defaultEncoding>utf-8</defaultEncoding>
...
</bean>

ข้อได้เปรียบที่เห็นได้ชัด สำหรับการใช้ DSL ในการ config spring ก็คือ
เราสามารถใส่ logic ลงไปได้ (เพราะมันเป็น code ไม่ใช่ xml)

ในส่วน function ที่สอง ที่มีการ inject method ให้กับทุก controller
ถ้าดูตาม code แล้วจะเห็นว่ามันทำแบบนี้
def doWithApplicationContext = { applicationContext ->
configureSendMail(application, applicationContext)
}

def configureSendMail(application, applicationContext) {
application.controllerClasses*.metaClass*.sendMail = {Closure callable ->
applicationContext.mailService?.sendMail(callable)
}
}

code กระทัดรัดมาก มันแค่ส่งต่อ closure ที่รับเข้ามาให้กับ mailService
ถ้าเป็น java code ก็ต้องยืดยาวประมาณนี้
MailService svr = (MailService) ctx.getBean('mailService');
if (svr != null) {
svr.sendMail(...);
}

Related link from Roti

No comments: