Thứ Tư, 10 tháng 8, 2016

Sử dụng filter trong J2EE application (Phần 1)

Sử dụng filter trong J2EE application (Phần 1)

Tác giả: Tăng Hải Ngọc Sơn – HunterBMT

Mục đích: Bài viết này là giúp các bạn nắm được cách sử dụng 1 thành phần khá hay trong J2EE mà có lẽ , bình thường chúng ta sẽ không dùng đến, đấy là filter.

Sử dụng filter trong J2EE application (Phần 1)

Tác giả: Tăng Hải Ngọc Sơn – HunterBMT

Mục đích: Bài viết này là giúp các bạn nắm được cách sử dụng 1 thành phần khá hay trong J2EE mà có lẽ , bình thường chúng ta sẽ không dùng đến, đấy là filter.

Yêu cầu :

·         Nắm rõ khái niệm về Filter cũng như mô hình MVC trong J2EE

·         Nắm rõ cách thức hoạt động của 1 web application .

·         Cài sẵn SQL 2005 có thể kết nối qua JDBC (phục vụ cho bài viết tiếp theo)

·         Các tool sử dụng :

+  Netbean 6.9.1

+  Java JDK 1.6.22

1.  Nhắc lại về Filter trong J2EE

     Filter (hay còn gọi là servlet filter) là một công cụ khá mạnh được cung cấp cho web developer từ phiên bản servlet 2.3 trở lên. Filter được thiết kế để giúp developer có thể xử lý request hoặc respone trước khi chúng được gửi tới servlet hoặc gửi về cho client . Các bạn có thể nhìn hình dưới đây để dễ hình dung hơn.

Chúng ta có thể hình dung , filter như một tấm fin pha café, để lọc trước khi chúng ta có những giọt cafe ở phía servlet .Nếu không có fin pha café, cốc café của chúng ta sẽ rất khó uống , vì café lẫn trong cặn chưa được gạt bỏ .

Câu hỏi đặt ra ở đây là , cái “cặn” chúng ta cần lọc trong một request gửi tới server là gì ? Và nếu chỉ lọc “cặn” tại sao chúng ta lại cần tới lọc cả 2 chiều ?

Nếu tôi hỏi “Filter dùng để làm gì ? “ Chắc chắn hơn phân nửa các bạn đang đọc vài viết này sẽ trả lời tôi một cách dễ dàng :”Để validation dữ liệu từ người dùng , để chèn thêm thông tin khi gửi tới người dùng . v.v.v ” . Nhưng có bao giờ các bạn tự hỏi , nếu filter chỉ dùng để validation dữ liệu, tại sao chúng ta không dùng java script để validation ngay từ phía client ? Mà phải đợi request lên tới server chúng ta mới validation dữ liệu ? Nếu filter chỉ dùng để chèn thông tin khi gửi tới người dùng , tại sao chúng ta không chèn thẳng vào template của JSP ?

Trong loạt bài viết này, chúng ta hay tạm quên đi những tác vụ của filter mà chúng ta vẫn nghe , vẫn đọc trong sách vở , tôi sẽ đưa các bạn tới vùng đất mới , mà ở đó, filter làm được nhiều hơn thế, mạnh mẽ hơn thế, và có ích hơn thế .

Bây giờ, chúng ta sẽ tới với năng lực đầu tiên của filter đó là  Nén

2. Nén webpage với filter

Nén webpage cũng tương tự như các bạn nén file bằng winrar vậy . Mục đích đều là làm giảm dung lượng của file , tối ưu khả năng lưu trữ, sao chép . Việc nén webpage mang lại cho chúng ta nhiều lợi ích hơn thế . Không những làm giảm băng thông cần thiết cho một respone ( giảm chi phí cho hosting ) , chúng ta còn có thể tối ưu hóa tốc độ load web page nhờ vào việc nén web page . Một website dù hay đến mấy, cũng không thể giữ chân người dùng nếu cứ mỗi cú F5 họ phải đợi tới vài phút, đúng không nào.

Lý thuyết thì là thế, nhưng chúng ta sẽ thực hiện như thế nào ? Chúng ta đều biết, trình duyệt chỉ có thể đọc được html , không đọc được file nén . Nếu chúng ta thực hiện nén file html rồi gửi về cho người dùng , điều đầu tiên chúng ta cần quan tâm, đó là trình duyệt sẽ đọc nó như thế nào .

Rất may , đa phần trình duyệt hiện nay của chúng ta đều hỗ trợ tự động giải nén, cho chuẩn GZIP . Nếu web page được nén theo dạng này , nó sẽ được trình duyệt giải nén tự động và hiển thị cho user như những web page bình thường . Do đó, điểm quan trọng đầu tiên chúng ta cần nghĩ , là làm sao để nén web page thành GZIP . Dĩ nhiên không thể dùng winrar hay những thứ tương tự rồi .

 

Có lẽ các bạn đang đợi một thuật toán nén file nào đó, và chúng ta sẽ implement nó trong Bean để sử dụng . Tuy nhiên , Java đã đi trước chúng ta nhiều bước, khi hỗ trợ nén GZIP thông qua package java.ulti.zip . Chúng ta sẽ sử dụng package này và class java.ulti.zip.GZIPOutputStream để nén web page của chúng ta mà chưa cần quan tâm tới nội dung bên trong từng class của nó ( chúng ta sẽ phân tích nó vào một bài viết khác, mang đậm tính học thuật hơn là hướng dẫn .)

Chúng ta sẽ bắt tay vào implement Filter này ( chúng ta sẽ đặt tên là GZIPFilter ) . Cũng như những filter khác, chúng ta sẽ implement javax.servlet.Filter interface và customized 2 class khác là javax.servlet.ServletOutputStream và javax.servlet.http.HttpServletRespone .

Chúng ta mở netbeans, chọn Java Web, tạo một project Web application mới với tên là demofilter

Trong Source Packages, chúng ta tạo một packages filter và 3 Java class GZIPFilter, GZIPResponseStream, GZIPResponseWrapper

Chúng ta sẽ implement class đầu tiên, GZIPFilter .

Các bạn implement class GZIPFilter như sau :

Các bạn có thể thấy , GZIPFilter của chúng ta  là một filter, do đó, chúng ta cần implements Filter . Netbean sẽ tự phát sinh các hàm cần thiết cho một filter như init() , destroy() , doFilter() , ở bài viết này mình sẽ không đề cập chi tiết cách thức làm việc của từng hàm , chúng ta chỉ nhắc lại góc nhìn cơ bản nhất . Khi Filter được mapping vào web.xml , nếu request phù hợp với những thông số đã mapping , nó sẽ đc chuyển tới filter trước khi tới servlet hay JSP .

Filter sẽ nhận gọi hàm init() một lần duy nhất, khi request đầu tiên được đưa tới . Sau đó request,respone sẽ được chuyển vào hàm doFilter() để thực thi .

Trong hàm doFilter() , những câu lệnh đằng trước chain.doFilter(req,res); là những câu lệnh sẽ được thực hiện trước khi request đến servlet hoặc JSP . Những câu lệnh đằng sau chain.doFilter(req,res) sẽ được thực hiện trước khi response được gửi trả về cho client .

Ở phần này, chúng ta chỉ đơn thuần nhận lấy response cần thiết, nén nó lại ,rồi gửi trả về cho client như bình thường.

Đầu tiên , các bạn có thể thấy ở phần khoanh đỏ thứ 2 . Chúng ta cần kiểm tra xem , trình duyệt của client có hỗ trợ GZIP hay ko . Các bạn có thể bỏ qua đoạn này, nếu chúng ta chắc chắn user sẽ sử dụng những trình duyệt mới, có hỗ trợ GZIP . Ở đây chúng tôi vẫn thực hiện nó, để không giới hạn trình duyệt user sử dụng .Chúng ta làm như bên dưới

Thông tin về định dạng encoding hỗ trợ, nằm trong request Header , do đó, chúng ta dùng hàm request.getHeader(“accept-encoding”) để lấy tập những định dạng encoding mà trình duyệt của user hỗ trợ .

Tiếp theo, chúng ta kiểm tra xem, trong tập này có tồn lại định dạng GZIP của chúng ta hay không .

Nếu tồn tại, chúng ta sẽ nén respone

Nếu không , chúng ta sẽ không làm gì cả

Hai hàm init() và destroy () chúng ta sẽ không thay đổi gì cả . Các bạn có thêm thêm các lệnh system.out.println(); để hiểu rõ hơn về life cycler của Filter .

Tiếp theo , chúng ta sẽ implement class GZIPResponseWrapper để thực hiện công việc nén response

Chúng ta thực hiện như sau

 

Như các bạn thấy ,class GZIPResponseWrapper của chúng ta kế thừa từ HttpServletResponseWrapper , và chúng ta cũng không có nhiều thay đổi lắm, so với nguyên bản trừ việc

Chúng ta sẽ sử dụng một OutputStream đặc biệt , để thực hiện việc nén respone, đây là hàm GZIPResponseStream của chúng ta .

Chúng ta tiếp tục implement hàm GZIPResponseStream kế thừa từ ServletOutputStream như sau :

Ở đây, chúng ta sử dụng class java.ulti.zip.GZIPOutputStream để thực hiện việc nén response

Việc sử dụng các hàm trong class có lẽ không quá khó khăn với các bạn , nên chúng tôi sẽ không đi sâu vào chi tiết của từ hàm trong class này . Các bạn có thể nhìn implement trên để hiểu ( theo chúng tôi là khá dễ dàng ).

Như vậy là chúng ta đã hoàn thành GZIPFilter để nén một response trước khi gửi về cho client . Theo chuẩn GZIP , dữ liệu của chúng ta sẽ đc nén theo tỉ lệ 6:1 , và trên thực tế, chúng ta sẽ giảm được khoảng 60% dung lượng của response. Việc còn lại , là chúng ta sẽ map nó vào tomcat thông qua web.xml

Vấn đề đặt ra ở đây, là tại sao chúng ta chỉ áp dụng nén với các trang jsp và html mà không phải là toàn bộ ? Nguyên nhân ở đây , là việc nén các file đã được nén ( jnp chẳng hạn ) hoặc các file đã đc mã hóa , không đơn giản như việc chúng ta chỉ nén file jsp hay html . Do đó, ở mức độ bài viết này, chúng ta sẽ chỉ thực hiện nén các file .jsp và .html để đảm bảo không có lỗi xẩy ra trong quá trình nén và giải nén .

 

Hy vọng bài viết đã giúp các bạn có một cái nhìn tổng quan về việc sử dụng filter một cách hiệu quả . Hẹn gặp lại các bạn ở phần 2 của bài viết :”Sửa dụng filter để tạo bộ đệm ở phía server”.

Mọi thắc mắc, các bạn có thể comment hoặc email về địa chỉ hunterbmt@gmail.com

1 nhận xét: