Thứ Tư, 31 tháng 7, 2013

Thao tác với properties file


Hỏi nhanh đáp gọn:
Làm sao để viết một properties trải ra trên nhiều dòng ?

Dùng ký tự \ để thông báo properties chưa kết thúc. Ví dụ:

mykey=This is \
             Demo

Làm sao để nhập unicode vào properties file ?
Bạn chỉ có thể nhập unicode vào value thôi. Key của properties nên là tiếng Anh không dấu. Trong JDK đã hỗ trợ sẵn công cụ này rồi. Bạn tìm trong thư mục $JAVA_HOME/bin sẽ thấy một executable file có tên là native2ascii. 

$JAVA_HOME/bin/native2ascii input output


Đây là một program mà nhận đầu vào là một file chứa mã unicode và đầu ra sẽ là một file chứa dữ liệu giải mã unicode. Bạn có thể dùng  các giá trị output này làm value cho các properties.

Thứ Hai, 29 tháng 7, 2013

How to use struts 1 validation framework ?

Struts validation framework rất đáng giá nếu bạn biết tận dụng. Nó giải quyết giúp bạn nhiều vấn đề mà các lập trình viên hay gặp phải như: Thực hiện validate data tại client qua mã java script hay thực hiện validate data tại action và cả việc quản lý message, error trả về từ action nữa. Mình sẽ phân tích một chút ít về hai tình huống này để thấy sự khổ sở của lập trình viên khi thực hiện mà không có Struts validation framework.

Trong tình huống thứ nhất, nếu bạn validate data tại client qua java script thì chương trình của bạn vẫn có thể bị đổ vỡ khi người dùng vô tình hay cố ý truy cập action của bạn qua address bar hoặc qua history. Những truy cập trực tiếp đó không được validate data nên chương trình của bạn sẽ không chống đỡ được. Giờ thì có thể bạn nghĩ sẽ phải validate data trên action thay vì tại client. Nhưng đó không phải giải pháp hay. Validate data không phải là business logic chính của chương trình. Validate data là rất cần cho tất cả các application và cần ở nhiều nơi trong chương trình nhưng nếu validate bừa bãi thì code của bạn sẽ bị lộn xộn và khó quản lý. Code xuất hiện nhiều nơi cho các mục đích tương đương sẽ gây ra duplicate code. Những code loại này được gọi là concern. Lập trình hiện đại hạn chế càng nhiều càng tốt các concern. Người ta sẽ tìm cách gom các concern code này vào một khu vực và có thể tham chiếu sử dụng chúng từ bất cứ nơi đâu trong chương trình. 

Xem xét tình huống thứ hai. Validate data tại action còn gặp trở ngại khi bạn cần trả thông báo về cho người dùng. Có nhiều cách để thực hiện trả thông báo mà không cần hỗ trợ của struts validation framework như nối tham số vào link gọi JSP. Tại JSP sẽ phải bắt và nhận diện tham số để xuất thông báo phù hợp. Cách khác là dựa vào ActionForward để trả về một trang chỉ chứa câu thông báo phù hợp với ActionForward đó. Sau đó include trang đó vào trang chính bằng ajax để hiển thị cho người dùng xem trên trang chính. Cả hai cách đều rất rắc rối và khiến cho việc quản lý thông báo khó khăn. Bạn phải chỉnh sửa tương đối trên cả action và jsp, struts-config cho mỗi một thông báo.

Struts validation framework giải quyết khó khăn trong cả hai tình huống. 

Vậy thì cần chuẩn bị những gì để áp dụng được struts validation framework vào project

Đầu tiên và tất nhiên là bạn phải chuẩn bị thư viện rồi. Mình khuyến cáo bạn sử dụng maven để tìm thư viện nhanh chóng. Sau đó bạn cần chuẩn bị một số file sau:
  • File properties, tên đặt tùy ý, thường thì nó có tên là MessageResources.properties.
  • File validation gốc, thường thì là validator-rules.xml. Bạn không sửa file này.
  • File validation của bạn, tên đặt tùy ý, thường thì là validation.xml
  • Thực hiện một số cấu hình để struts nhận diện cả ba file trên.
File properties chứa key và value cho các thông báo hệ thống. Nó phải chứa bộ properties gốc của struts:


# -- validator --
errors.invalid={0} is invalid.
errors.maxlength={0} can not be greater than {1} characters.
errors.minlength={0} can not be less than {1} characters.
errors.range={0} is not in the range {1} through {2}.
errors.required={0} is required.
errors.byte={0} must be an byte.
errors.date={0} is not a date.
errors.double={0} must be an double.
errors.float={0} must be an float.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.short={0} must be an short.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
errors.url={0} is not a valid URL.


File validator-rules.xml chứa các default rules của struts như required, minlength, maxlength. Bạn lấy file này ở đâu ? Mình không biết :D IDE của mình hỗ trợ nên nó đưa cho mình luôn.

File validation.xml chứa các user-defined rules của bạn. Mình sẽ giải thích chi tiết nội dung file này sau.

Cấu hình struts-config cần thiết:
    
    <message-resources parameter="MessageResources" null="false" key="globalMsg" />


    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"
                      property="pathnames"/>
    </plug-in>

Vị trí validator-rules.xml và validation.xml đều nằm trong /src/main/webapp/WEB-INF. Còn MessageResources.properties thì nằm trong /src/main/resources. Có thể bạn thấy hơi lạ nhưng đây là cấu trúc web project trong maven. Mình sẽ nói về maven trong một entry khác.

Xem xét thẻ message-resource:
Trong thẻ message-resources có thuộc tính null="false". Thuộc tính này sẽ hướng dẫn struts validation framework xử lý các properties mà missing key xuất ra thông báo:  ' ???keyname??? ' thay vì hiện null. Mặc định thuộc tính này là null="true" và đây không phải là thuộc tính bắt buộc.

Thuộc tính parameter="MessageResource" là đường dẫn đến vị trí file properties. MessageResource là tên file properties. Đây là thuộc tính bắt buộc.

Thuộc tính key="globalMsg" dùng để phân biệt các properties với nhau. Tại sao phải phân biệt qua key vì giá trị key này sẽ được dùng để xác định file properties nào được tham chiếu từ thẻ <html:errors> Thẻ này phân biết các properties qua key chứ không phải bản thân tên file properties.

Thẻ plug-in không có gì để bàn vì rất dễ hiểu rồi.


Struts validation framework hỗ trợ thực hiện validation theo form hoặc action. 


Trường hợp validation theo form: Bạn sẽ kế thừa form bean của bạn từ ValidationForm
Trường hợp validation theo action: Bạn sẽ kế thừa form bean của bạn từ ValidationActionForm

Khác biệt giữa hai class này là: Khi kế thừa ValidatorForm, struts validation framework sẽ validate form của bạn theo formbean mà action của bạn sử dụng còn khi kế thừa ValidatorActionForm, struts validation framework sẽ validate form của bạn theo action. 

Lý do cần có ValidatorActionForm vì một form bean có thể được sử dụng chung bởi nhiều action. Thường thì lập trình viên hay tạo một form bean chung và nhét tất cả các trường dữ liệu cần đưa lên action vào đó. Nhưng cách sử dụng form bean của các action khác nhau thì khác nhau. Có action cần validate toàn bộ các trường khai báo trong form bean nhưng lại có action chỉ cần validate một phần các trường dữ liệu thôi. Ví dụ: Bạn có hai action createUser.do và changePass.do Action createUser.do cần validate nhiều hơn changePass.do Từ đó có thể thấy cách validate sẽ phải tương ứng với từng action dùng form bean thay vì cố định vào một form bean.

Bạn cần chỉ định action nào cần được validate trong struts-config


        <action path="/createUser" type="org.springframework.web.struts.DelegatingActionProxy"
                name="userForm" scope="request" validate="true" input="/inputUser.do">
            <forward name="create" path="/jsp/user/list.jsp"/>
        </action>


Thuộc tính validate="true". Action nào không cần validate thì set là false

Thuộc tính input dùng để trỏ về action hoặc jsp chứa form đầu vào của action. 

Ví dụ: Bạn có hai action. Action createUser.do để lưu user đó vào db còn một action là inputUser.do để hiện form nhập liệu. Khi đó createUser.do sẽ là action cần validate và thuộc tính input="/inputUser.do" Khi validate thất bại, createUser.do sẽ thực hiện forward đến inputUser.do Tốt nhất là tách ra hai action như vầy, vì có action cần validate, có action thì không cần và trong các action cần validate lại có các input khác nhau.

Luồng thực hiện khi có một request đến action /createUser.do như sau:


Struts validation framework sẽ validate form trong request theo các rules mà bạn khai báo trong validation.xml. Nếu thất bại nó sẽ trả lại /inputUser.do Trường hợp này, request của bạn sẽ chưa đến được /createUser.do Ngược lại, nếu validation thành công, request sẽ đi thẳng đến /createUser.do và tiếp tục được thực hiện xử lý như bình thường.

Gần như là xong rồi, bạn cần xây dựng rules nữa. Tham khảo:   http://struts.apache.org/release/1.2.x/userGuide/dev_validator.html

Để hiển thị messages trên jsp, bạn sử dụng thẻ <html:errors /> Nên kết hợp với cặp thẻ <logic:messagesPresent></logic:messagesPresent>

<logic:messagesPresent>
            <html:errors bundle="globalMsg" />
</logic:messagesPresent>

Đoạn mã trên có nghĩa là sẽ kiểm tra xem có message trong attribute của request hay không. Nếu có sẽ dịch thẻ <html:errors /> Thuộc tính bundle có giá trị khớp với key của MessageResources mà bạn đã khai báo.

Sức mạnh của struts validation framework chưa dừng ở đó. Bạn có thể tận dụng cơ chế truyền tải message về jsp của nó để hiển thị thông báo trong action của bạn sau khi validation. Giả sử bạn được validation thành công. Request của bạn đến /createUser.do Tại đây, /createUser.do cần kiểm tra xem username có trùng lặp không. Nếu trùng lặp, /createUser.do sẽ tạo một ActionMessages và đẩy vào request attribute để hiển thị trên jsp.


ActionMessages errors = new ActionMessages();
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(key, arg);
saveErrors(request, errors);

Ngoài saveErrors(request, errors) còn có addErrors(request, errors). Khác biệt giữa chúng là saveErrors(request, errors) sẽ xóa sạch các action messages hiện tại và ghi action message vừa tạo ra vào còn addErrors(request, errors) chỉ đơn giản nối thêm vào.


Thêm một chú ý nữa: Để message của bạn hiển thị trên jsp, bạn không được redirect="true" cho ActionForward trả về vì redirect="true" sẽ gửi về client mã 302 để client tạo một request khác đến đích khuyến nghị trong response có status 302.

Hướng dẫn trên là cho cách truyền tải errors messages từ action về client nhưng bạn có thể dùng cách làm đó để truyền tải các messages thông thường. Tất nhiên, struts có hỗ trợ cách thức riêng cho truyền tải các messages thông thường. Tuy nhiên các messages đó đều được hiểu là plain text, bạn không thể mong nhúng thẻ html vào message và hi vọng messages sẽ được hiển thị ở dạng html.

Ví dụ: Thông bảo của bạn là <strong>Exception</strong> chả hạn nếu bạn dùng cách truyền tải thông điệp kiểu errors thì bạn sẽ nhận được chữ Exception được in đậm còn nếu bạn dùng cách truyền tải thông điệp kiểu message thì sẽ nhận được chữ <strong>Exception</Exception>

How to use in-memory database h2 with hibernate

H2 là một database engine rất hữu ích khi bạn phát triển sản phẩm vì tính nhanh gọn trong cài đặt và triển khai của nó. Nhưng nó không phải là một cơ sở dữ liệu thích hợp trong môi trường production. Lợi ích đáng giá của H2 là chế độ in-memory database. Điều này có nghĩa là bạn tạo một cơ sở dữ liệu nằm gọn trên memory RAM của bạn. Trong trường hợp này, cơ sở dữ liệu về cấu trúc cũng như nội dung sẽ không được persistent.


Mặc định, in-memory database h2 sẽ tự động close khi last connection đến database được release. Khi database close thì cấu trúc và nội dung của nó sẽ bị xóa khỏi bộ nhớ. Trong bài viết này mình sẽ hướng dẫn bạn phương hướng (không chi tiết) về cách hình thành data source trong IDE sử dụng in-memory database (private) và in-memory database (named). Cuối cùng sẽ là cách sử dụng hai  kiểu database này với hibernate.



Phân biệt: in-memory database (private) và in-memory database (named)



in-memory database (private): Bạn sẽ khai báo một db URL lược bỏ phần tên db đi. Loại db này chỉ chấp nhận một connection duy nhất. H2 sẽ tạo một database mới cho connection thứ hai hướng đến db URL này. URL của loại db này trông như sau:  jdbc:h2:mem: Vì db nằm trong memory nên sẽ không có username, password.



in-memory database (named): Khác với loại db trên, loại db này có xác định một tên cụ thể cho db. Loại db này chấp nhận nhiều connection. Hai connection cùng hướng đến URL của loại db này sẽ cùng được kết nối đến cùng một database trong điều kiện cùng virtual machine. URL của loại db này trông như sau: jdbc:h2:mem:<databaseName>



Khi nào thì dùng named db khi nào dùng private db ?



Tùy yêu cầu application của bạn. Ví dụ bạn tạo một Java application. Bạn chạy application đấy trong JRE. Nếu application cần nhiều connection cùng lúc đến in-memory h2 database thì bạn sẽ cần loại named db.



Để tạo datas source trong IDE đến named in-memory h2 db, bạn cần:


  • h2-1.3.173.jar
  • driver class: org.h2.Driver
  • url: jdbc:h2:mem:sample_db # sample_db là tên của db
  • username: không cần, để rỗng
  • password: không cần, để rỗng

Sau khi thiết lập data source, bạn chạy script để thiết lập cấu trúc cho db. Thế là xong.


Để làm việc named h2 db với hibernate, bạn cần nhiều hơn thế.



H2 db luôn tự động close db khi last connection release. Để giữ db luôn open phục vụ các connection tiếp theo, bạn cần gài thêm tham số DB_CLOSE_DELAY=-1 sau db URL. 



H2 còn tự động close db khi JVM exit. Điều này có nghĩa là sau mỗi lần chạy ứng dụng xong, db của bạn sẽ bị xóa sạch. Để giữ cho db của bạn open cho lần chạy ứng dụng tiếp theo, bạn cần gài tiếp thêm tham số DB_CLOSE_ON_EXIT=FALSE sau db URL



Còn một tham số nữa dùng để tăng độ tin cậy khi thiết lập kết nối đến db là IFEXISTS. Tham số này sẽ kiểm tra xem liệu db khai báo theo URL có tồn tại hay không. Mặc định khi application thực hiện DriverManager.getConnection(url, ...), nếu db chưa tồn tại, h2 sẽ tạo một empty db cho application. Nếu bạn không muốn h2 tự động làm điều này thì bạn chỉ cần gài tham số IFEXISTS=TRUE sau URL. Một cố gắng kết nối đến db chưa tồn tại sẽ khiến cho exception được ném ra.



Khi chạy hibernate application, hibernate framework sẽ chỉ đơn giản load *.hbm.xml và map với entity class. Nó giả định là bạn có db rồi, cấu trúc db đã có rồi. Nhưng chạy db in-memory, db này sẽ hình thành trong khu vực memory mà được cấp cho application của bạn khi chạy. Bạn không thể tạo ra nó từ trước, bạn chỉ có thể tạo ra nó khi chạy application của bạn. Làm thế nào đây:


  • Cần gán IFEXISTS=FALSE để h2 tạo một empty db cho bạn
  • Sử dụng tính năng execute SQL on connection, bạn gán thêm đoạn này để load và run script trước khi sử dụng: INIT=RUNSCRIPT FROM '~/create.sql'



Tóm lại, để sử dụng h2 với hibernate, bạn cần điều chỉnh đoạn URL như sau:

jdbc.url=jdbc:h2:mem:sample_db;INIT=RUNSCRIPT FROM '<đường dẫn tuyệt đối đến script file của bạn trên file system>';DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;IFEXISTS=FALSE;


Script file của bạn chỉ cần chứa các lệnh SQL để tạo tables để hình thành cấu trúc cho db. Vả lại bạn tìm trong h2 cũng không thấy lệnh SQL tạo db đâu.

Thứ Hai, 22 tháng 7, 2013

Trick sha-bang line

Sha-bang line là dòng đầu tiên trong mỗi shell script. Dòng này sẽ xác định shell interpreter nào sẽ được dùng để thông dịch script. Thông thường bạn sẽ thấy ở dòng này sẽ trỏ đến các shell interpreter phổ biến: #!/bin/bash Khi đó, bạn chạy script cũng tương đương bạn chạy /bin/bash <script-name>.sh

Vậy thì chúng ta có thể trick gì ở đây được. Nếu bạn thử thay đổi sha-bang line thành #!/bin/rm thì sao :D Khi đó, bạn chạy script cũng tương đương bạn chạy /bin/rm <script-name>.sh Thay vì bạn chạy script thì bạn lại xóa script của chính bạn :D

Heap vs Stack

Heap và stack đều là hai khu vực bộ nhớ rất quen thuộc nhưng vai trò của chúng thì khác biệt nhau hẳn. Lượt qua cái xem nhé:

Heap: Là khu vực bộ nhớ cấp phát động, có thể được cấp phát thêm khi đạt đến giới hạn
Các thành phần data như object, instance variable, class variable đều nằm trong heap. Khi bạn tạo quá nhiều object sẽ rất dễ tràn bộ nhớ Heap: OutOfMemoryError.

Stack: Là khu vực bộ nhớ có giới hạn được cấp phát để lưu local variable (tham số truyền vào một method hoặc là các biến được khai báo bên trong một method), reference variable và method call. Khi bạn sử dụng giải thuật đệ quy không khéo léo sẽ rất dễ tràn bộ nhớ stack vì số lượng method call không kiểm soát được: StackOverFlowError.

Trong lập trình thread: Stack là phần bộ nhớ private của mỗi thread, thread khác không xâm phạm được còn heap là phần bộ nhớ chia sẻ. Các thread có thể lần lượt truy xuất, thay đổi khu vực dữ liệu này. Để đảm bảo không có xung đột, trong lập trình multithread, lập trình viên chúng ta thường cần phải sử dụng các cơ chế đồng bộ.

GC đối xử với các bộ nhớ này như thế nào ?
GC chuyên dùng để dọn dẹp bộ nhớ heap. Bất cứ khi nào một object không còn được tham chiếu nữa thì nó sẽ bị GC xem xét để xóa bỏ. Riêng với object là thread thì GC chỉ được xem xét xóa bỏ nó khi trạng thái của thread object isAlive() = false.

Ngoài hai bộ nhớ stack và heap, trong Java còn có một loại bộ nhớ nữa gọi là Perm. Đây là khu vực chứa class metadata. Bạn có thể thấy có trường hợp vùng nhớ này bị quá tải khi deploy ứng dụng lên tomcat. Mỗi lần deploy ứng dụng, class metadata của ứng dụng đó lại được sinh thêm ra, trong khi đó các class metadata cũ lại không được dọn dẹp nên dẫn đến tình trạng quá tải. Khi gặp tình huống này thì bạn chỉ còn cách restart tomcat để dọn sạch vùng nhớ perm đang bi Tomcat process chiếm dụng.

Thứ Bảy, 20 tháng 7, 2013

Introduction to thread local

Có thể bạn đã biết hoặc chưa biết về thread local nhưng như mình đã nói trong bài viết đầu tiên Buffer vs Cache có gì hay ho thì mình sẽ kể cho bạn nghe. Ở đây, trong khuôn khổ bài viết này, mình chỉ viết về thread local trong ngôn ngữ lập trình Java.

Thread local là một kỹ thuật khá là hay ho trong Java. Kỹ thuật này giúp lập trình viên giảm được các code thừa trong chương trình. Giảm code thừa rất quan trọng. Nó khiến chương trình dễ đọc và như thế dễ bảo trì, dễ mở rộng tính năng hơn. Một số bạn cho rằng giảm code thừa sẽ khiến chương trình nhỏ gọn hơn do đó hiệu suất hơn. Điều này cũng đúng nhưng mình nghĩ cái đáng giá của những kỹ thuật như thread local là giúp lập trình viên viết code dựa trên một cơ chế được hỗ trợ từ chính ngôn ngữ nên sẽ tận dụng được sức mạnh của ngôn ngữ đó. Chính điều này sẽ nâng cao hiệu suất của chương trình.

Thread local sẽ định ra mức truy xuất global và local đến một object bên trong một thread.

- Mức truy xuất global được định ra cho các method bên trong thread đến thread local. Bạn sẽ không cần truyền object đó đến từng method cần sử dụng. Kỹ thuật thread local sẽ giúp cho mọi method trong thread luôn có thể nhìn thấy object.

- Mức truy xuất local được định ra cho các thread khác. Những thread khác sẽ không thể truy xuất đến thread local của một thread. Nói cách khác object trong thread local sẽ không thể được nhìn thấy bên ngoài ranh giới của thread mà sở hữu nó.


Bây giờ tôi sẽ viết một chương trình đơn giản để minh họa. Giả sử có một kịch bản như sau: client gửi request đến service. Service sẽ gen một request id cho mỗi request. Bạn cần phải sử dụng request id này trong hầu hết các service method của service. Với yêu cầu này, thông thường bạn sẽ nghĩ đến việc truyền service id vào tất cả các service method cần sử dụng. Nhưng thử áp dụng khái niệm thread local vào đây, bạn sẽ không cần truyền request id nữa.


Bạn cần bốn classes. Tất cả đều đặt trong package com.java.underground.thread

Class đầu tiên có tên là Context:















Context là một đối tượng giữ các thông tin chung cần được tham chiếu nhanh chóng. Ví dụ: SecurityContext trong sping security chứa các thông tin về authentication hoặc ServletContext trong servlet container. Một container thì lại là một thùng chứa object và cung cấp các tiện ích thao tác trên các object đó.

Class thứ hai là MyThreadLocal:

















ThreadLocal được cung cấp sẵn trong Java language. Bạn chỉ cần biết để sử dụng thôi. MyThreadLocal cung cấp các method để set, get, unset object

Class thứ ba là ThreadLocalExample:























Bạn tao ra hai thread gọi là threadOne và threadTwo. Trong mỗi thread bạn tạo ra một context object. Bạn đặt context object vào thread local sử dụng các phương thức tĩnh của thread local nhờ vậy bạn có thể thực hiên doBusinessMethod() mà không cần truyền đối tượng context theo.

Class thứ tư là BusinessService:










Bạn có thể truy xuất đến context qua thread local. Bạn không cần truyền context theo. Bạn có thể thắc mắc làm sao JVM biết được thread local nào của thread nào. Dù MyThreadLocal được thiết kế là static nhưng nó được sử dụng ở trong thread nào thì sẽ local trong thread đó mà thôi. 

Kết quả bạn có thể đoán được là:

Output:
request id of thread: Thread-1
request id of thread: Thread-0


Nói qua về thread: Thread là một object nhưng là một object được sử dụng để tạo ra môi trường thực thi cho các object khác. Một object là thread thì GC sẽ không có quyền clean nó cho đến khi isAlive() của nó trả về false. Việc thực hiện thread không tuần tự như bạn khai báo. Như ví dụ trên bạn có thể thấy Thread-0 được khai báo trước nhưng lại chạy sau Thread-1. Lý do là vì scheduler - một thành phần trong kernel sẽ đảm nhận việc chọn thread nào để chạy. Không ai đoán được scheduler sẽ chọn thread nào nên lập trình multithread rất khó. Code trong mỗi thread lại chạy tuần tự khi thread đó được gọi thực hiện. Kể cả khi không lập trình multithread thì trong chương trình Java bạn viết luôn có một thread gọi là main thread: Một process có thể có nhiều thread và luôn có ít nhất một thread. Trước khi làm quen với multithread, bạn có thể thấy code trong main thread luôn được gọi tuần tự.


Thôi nhé, tôi đi ăn cơm đấy, đói quá rồi :D 

Tạm biệt. Hi vọng bài viết này có ích cho bạn

Tham khảohttp://java.dzone.com/articles/java-thread-local-%E2%80%93-how-use