Làm chủ Grails: Xây dựng ứng dụng đầu tiên với Grails pot

16 464 1
Làm chủ Grails: Xây dựng ứng dụng đầu tiên với Grails pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Làm chủ Grails: Xây dựng ứng dụng đầu tiên với Grails Hãy xem sức mạnh lớn thế nào trong một bộ khung web nhỏ bé này Scott Davis , Tổng Biên tập, AboutGroovy.com Tóm tắt: ™Các nhà lập trình Java không cần từ bỏ ngôn ngữ yêu thích của họ và các cấu trúc hạ tầng phát triển đang có để thực hiện một khung phát triển web hiện đại mới. Trong lần đăng báo đầu tiên của loạt bài hàng tháng Làm chủ Grails - Mastering Grails, chuyên gia về Java Scott Davis đưa ra Grails và trình diễn làm thể nào để thiết kế ứng dụng Grails đầu tiên của bạn. Cho phép tôi được được giới thiệu bạn với Grails bằng cách của một khung phát triển web nguồn mở: Ruby on Rails. Khi Rails mới được công bố, nó thu hút các nhà phát triển. Khả năng nền tảng đỡ của Rails giúp bạn tự xây dựng một dự án mới trong khoảng thời gian ít hơn nhiều so với trước đây. Quy ước so với cấu hình ý tưởng của từ Rails có nghĩa là ứng dụng của bạn tự động ràng buộc nó lại dựa trên các chương trình dễ hiểu và có tên chứ không phải bằng các tệp cấu hình xml hay bị lỗi và buồn tẻ. Khả năng siêu chương trình của Ruby cho phép đối tượng được phần nào thừa hưởng các phương thức và các trường cần thiết để vận hành mà không làm tắc nghẽn mã nguồn. Rails đã từng (và vẫn) xứng được phong tước và ngưỡng mộ, nhưng nó đưa ra cho các nhà phát triển Java một lựa chọn khó khăn. Liệu có phải từ bỏ nền Java quen thuộc để đổi lấy một cái mới đầy hứa hẹn và sẽ phải làm gì với các mã Java đã tồn tại hiện nay, với các máy chủ sản phẩm hiện nay, và với các cán bộ phát triển Java có kinh nghiệm? Về loạt bài viết này Grails là một khung phát triển web hiện đại gắn với các công nghệ quen thuộc của Java như Spring và Hibernate cùng với các kinh nghiệm thực tiễn đương thời như quy ước so với cấu hình. Như đã viết trong Groovy, Grails tạo cho bạn sự tích hợp thông suốt đối với mã Java có sẵn mà vẫn tăng thêm tính linh hoạt và năng động của ngôn ngữ viết. Sau khi bạn đã học Grails bạn sẽ không bao giờ nhìn phát triển Web theo cách cũ nữa. Gia nhập Grails. Grails cho bạn kinh nghiệm phát triển của Rails trong khi vẫn gắn chặt với công nghệ đã được thừa nhận của Java. Nhưng Grails không phải là cổng đơn giản kiểu "tôi cũng vậy" của Rails đối với nền Java. Grails rút ra bài học từ Rails và trộn chúng với sự tinh tế của phát triển Java hiện đại. Ý nghĩ được truyền cảm hứng, chứ không được chuyển dịch. Để bắt đầu loạt bài Làm chủ Grails, bài viết này giới thiệu bạn với nền Grails, cho bạn thấy làm thế nào để cài đặt nó và qua việc xây dựng ứng dụng Grails đầu tiên: một chuyến lập kế hoạch mà bạn sẽ tiếp tục làm việc với các bài viết sau của loạt bài này. Sức mạnh của Groovy Do Rails gắn chặt chẽ với ngôn ngữ lập trình Ruby, Grails sẽ không thể thoát ra nếu không có sức mạnh to lớn của Groovy (xemTài nguyên). Groovy là một ngôn ngữ động chạy trên JVM và hòa nhập thông suốt đối với ngôn ngữ Java. Nếu bạn đọc loạt bài dài Practically Groovy của developerWorks, bạn đã làm quen với sức mạnh của ngôn ngữ lập trình này. Nếu bạn chưa đọc, thì đừng lo — khi bạn học Grails, bạn sẽ có rất nhiều Groovy trong khi học. Nó sẽ không khó lắm do Groovy được chủ định thiết kế riêng cho các nhà phát triển JAVA. Ví dụ, Groovy cho phép bạn giảm mạnh mẽ số lượng mã JAVA mà thông thường bạn phải viết. Bạn cũng không cần viết phần thu và phần cài (getters and setters) cho các trường của bạn, do Groovy cung cấp chúng cho bạn một cách tự động. Không còn phải viết vòng lặp for Iterator i = list.iterator() qua một danh sách các khoản mục list.each làm như vậy một cách chính xác và quyết liệt. Nói một cách đơn giản, Groovy chính là ngôn ngữ Java sẽ trở thành nếu nó được viết trong thế kỷ thứ 21. Groovy sẽ không hấp dẫn tý nào đối với các nhà phát triển Java nếu nó bắt họ phải viết lại toàn bộ ứng dụng từ nền ban đầu để có thể tận dụng được ưu thế của nó. May mắn là, Groovy có thể tích hợp thông suốt đối với mã nền hiện nay của bạn. Groovy không thay thế ngôn ngữ Java — Nó là một bộ phận hỗ trợ phát triển. Bạn có thể học Groovy nhanh bởi vì cuối cùng, mã Groovy chính là mã Java. Hai ngôn ngữ này rất dễ tương thích với nhau nên bạn có thể đổi tên tệp khi làm việc là tệp .java thành tệp .groovy — ví dụ, thay đổi Person.java thành Person.groovy — để có giá trị hoàn hảo (và chạy được) như tệp Groovy (mặc dù tệp này không tận dụng được lợi ích từ các ngôn ngữ ngọt ngào mà Groovy đem lại). Mức độ tương thích cao này giữa ngôn ngữ Groovy và Java có nghĩa rằng Grails không phải kiến tạo lại bộ máy vận hành khi nó gặp phải các công nghệ chủ đạo đã được sử dụng trong môi trường hiện nay. Thay vào đó, nó cho phép bạn sử dụng thư viện quen thuộc của JAVA qua các nhóm Groovy-colored. JUnit TestCase được bọc trong Groovy và giới thiệu như là GroovyTestCase. Grails đưa ra một điều chỉnh mới trong Ant xây dựng với GANT, một thực hiện thuần Groovy của Ant. Grails gói Hibernate trong một lớp ngoài Groovy mỏng và gọi nó là GORM — the Grails Object/Relational Mapper. Đây chỉ là 3 ví dụ về Grails đã cho phép bạn cân bằng tất cả kinh nghiệm Java đã có trong khi tận dụng ưu điểm các thực tiễn phát triển web hiện đại. Để đánh giá được đầy đủ Grails, bạn phải trực tiếp thử nghiệm nó. Bây giờ là thời điểm cài đặt Grails và tạo ra ứng dụng Web đầu tiên của bạn. Cài đặt Grails Tất cả mọi thứ bạn cần để chạy ứng dụng Grails đã nằm trong một tệp nén (ZIP) duy nhất. Tất cả các thư viện trực thuộc — Groovy, Spring, và Hibernate, chỉ gọi vài tên — đã có sẵn và sẵn sàng sử dụng. Để cài Grails: 1. Tải và giải nén grails.zip từ địa chỉ của Grails (xem Tài nguyên). 2. Tạo biến môi trường (environtment variable) GRAILS_HOME. 3. Thêm $GRAILS_HOME/bin vào PATH. Tốt rồi, bạn rất cần cài một JDK. (Grails thì tốt nhưng nó không tốt quá.) Grails 1.0 chạy trên Java 1.4, 1.5, và 1.6. Nếu không biết bạn đã cài phiên bản nào thì gõ java -version tại dấu nhắc câu lệnh. Nếu cần, tải và cài bản Grails-compatible của JDK (xem Tài nguyên). Khi kết thúc các bước cài đặt, gõ grails -version để kiểm tra kết quả. Nếu bạn được chào với một biểu ngữ thân thiện, tức là mọi việc đã được cấu hình chính xác: Welcome to Grails 1.0 - http://grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: /opt/grails Máy chủ Web và cơ sở dữ liệu trong đó Sử dụng freebies Đối với ứng dụng của bài báo này, bạn sẽ sử dụng máy chủ web và cơ sở dữ liệu mà Grails cung cấp miễn phí. Trong các bài viết sau này, tôi sẽ cung cấp cho các bạn hướng dẫn từng bước một để chạy Grails ngược so với các máy chủ khác của bạn. Trong khi đó, không phải lo lắng về việc lang thang trên grails.org và tìm kiếm các tài liệu trực tuyến (xem Tài nguyên). Thú vị là, bạn không cần phải tách riêng máy chủ web đã cài để chạy ứng dụng Grails. Grails tự chuyển bộ chứa Jetty servlet đã được gắn vào. Bạn gõ grails run- app để có một ứng dụng và chạy trong bộ chứa Jetty (xem Tài nguyên) không phải chuyển qua những giai đoạn triển khai thông thường. Chạy ứng dụng Grails trên máy chủ hiện có của bạn cũng không thành vấn đề. grails war tạo ra một tệp chuẩn mà bạn có thể triển khai đối với Tomcat, JBoss, Geronimo, WebSphere® hoặc bất kỳ phần mềm chứa Java EE 2.4-compliant servlet nào. Bạn không cần phải có cơ sở dữ liệu cài riêng biệt. Grails chuyển tải với HSQLDB (xem Tài nguyên) một cơ sở dữ liệu thuần Java. Sau khi đã có một cơ sở dữ liệu có sẵn từ bên ngoài, bạn có thể ngay lập tức tăng hiệu suất làm việc. Sử dụng một cơ sở dữ liệu khác, như là MySQL, PostgreSQL, Oracle Database, hoặc DB2 là đơn giản bởi do Hibernate và GORM. Nếu bạn có một JDBC ổ đĩa JAR và cài kết nối thông thường trong tay, khi đổi sang DataSource.groovy, bạn có thể giao tiếp với cơ sở dữ liệu của bạn ngay lập tức. Viết ứng dụng Grails đầu tiên của bạn Tôi đi lại rất nhiều — tối thiểu 40 chuyến trong một năm. Tôi thấy rằng lịch trình đó đã cho tôi biết khi tôi cần phải đi đâu đó, nhưng lại không chỉ ra địa điểm đó ở đâu. Các bản đồ trực tuyến lại có vấn đề ngược lại: chúng làm tốt với việc chỉ đường, nhưng lại làm không tốt với việc thời gian lúc nào đi. Do đó trong bài này và một số bài tiếp theo của loạt bài viết này, bạn sẽ thiết lập một ứng dụng Grails đặc thù có thể giúp bạn về cả thời gian và địa điểm để lập kế hoạch chuyến đi. Thông báo tin Bạn có thấy liệu rằng các bài viết trong tương lai của loạt bài viết này sẽ trình bày về Grails gắn với Google Calendar và Google Maps? Tôi chắc sẽ như vậy Để bắt đầu, trong một thư mục trống và gõ grails create-app trip-planner. Sau một đợt các thao tác, bạn sẽ thấy một thư mục tên là người lập kế hoạch chuyến đi (trip-planner). Tương tự Maven, Rails và AppFuse, Grails tạo ra nền tảng một chuẩn cấu trúc thư mục cho bạn. Nếu bạn thấy hoàn toàn mất hy vọng do bị kiềm chế bởi sự hạn chế này và không thể làm việc với khung trừ khi bạn thiết kế tỉ mỉ cây thư mục riêng của bạn, bạn sẽ không còn hứng thú làm việc với Grails. Một phần quy ước của quy ước so với truyền thống sẽ cho phép bạn làm việc với bất kỳ ứng dụng của Grails và nắm ngay được đơn vị cơ sở (bit) nào được lưu giữ trong khu vực (bucket) nào. Thay đổi đối với thư mục người lập kế hoạch chuyến đi và gõ grails create- domain-class Trip. Nếu mọi việc thuận buồm xuôi gió, bạn sẽ có hai tệp mới đang chờ: grails-app/domain/Trip.groovy và grails- app/test/integration/TripTests.groovy. Tôi sẽ nói về phần kiểm tra trong bài viết sau. Hiện giờ, tôi tập trung vào nhóm miền, lúc mới đầu nó trông như trong Ví dụ 1: Ví dụ 1. Nhóm vùng tạo ra từ Grails class Trip{ } Không có gì nhiều để xem ở đây, chúng ta hãy bắt tay triển khai. Tạo trường vào trong chuyến đi (Trip) như trong Ví dụ 2: Ví dụ 2. Lớp Trip với các trường được thêm vào class Trip { String name String city Date startDate Date endDate String purpose String notes } Như tôi đã nói ở trên, bạn không phải lo lắng để tạo ra getters và setters: Groovy linh hoạt tạo ra chúng cho bạn. Trip cũng có rất nhiều phương thức mới linh hoạt hiệu quả với các cái tên tự nói lên tất cả:  Trip.save() lưu dữ liệu vào bảng Trip trong cơ sở dữ liệu HSQLDB.  Trip.delete() xoá dữ liệu từ bảng Trip.  Trip.list() đem lại một danh sách các Trip.  Trip.get() đem lại một Trip. Tất cả các phương thức này ngày càng đơn giản khi bạn cần chúng. Chú ý rằng Trip không mở rộng cho nhóm bố mẹ hoặc thực hiện một giao diện thần kỳ. Do năng lực siêu chương trình của Groovy, những phương thức này xuất hiện trong vị trí phù hợp trong các nhóm phù hợp. (Chỉ các nhóm trong thư mục grails- app/domain có các phương thức liên quan đến tính liên tục này) Xây dựng trình điều khiển và khung nhìn Tạo ra nhóm miền chỉ là nửa cuộc chiến. Mọi mô hình đều cần có một trình điều khiển (controller) tốt và vài khung nhìn (views) để một số thứ thể hiện. (Tôi giả định rằng bạn đã làm quen với mô hình Model-View-Controller; xem Tài nguyên. Đánh grails generate-all Trip để thiết kế ra một lớp grails- app/controllers/TripController.groovy và một bộ phù hợp của Groovy Server Pages (GSPs) trong grails-app/views/Trip. Đối với các danh sách hoạt động trong trình điều khiển, có một danh sách tương ứng tệp .gsp được tạo ra, một create.gsp. Ở đây bạn thấy lợi ích của quy ước so với cấu hình trong hoạt động: không cần có tệp XML để cân bằng lại với các phần tử này. Mọi lớp miền cặp với một trình điều khiển dựa vào tên. Mọi hoạt động trong một trình điều khiển phù hợp với một khung nhìn dựa vào tên. Bạn có thế tránh kiểu cấu hình dựa vào tên này nếu bạn thích, nhưng phần lớn thời gian bạn đi theo quy ước và ứng dụng của bạn có thể hoạt động được. Chú ý xem grails-app/controller/TripController.groovy, trình bày trong Ví dụ 3: Ví dụ 3. TripController class TripController { def list = { if(!params.max) params.max = 10 [ tripList: Trip.list( params ) ] } } Điều đầu tiên các nhà phát triển Java thường chú ý là với chỉ vài dòng mã thì có thể làm được bao nhiêu. Thực hiện danh sách hành động, là một ví dụ. Điều quan trọng tiếp theo trong phương thức này nằm tại dòng cuối cùng. Grails sẽ chuyển lại một sơ đồ chưa hoàn chỉnh cùng một yếu tố đơn nhất tên là tripList. Dòng cuối cùng của phương thức Groovy là một câu lệnh quay trở lại ẩn (a implicit return statement). Bạn có thể đánh bằng tay chữ: return nếu bạn thích. Thành phần tripList là một ArrayList (danh sách mảng) của các đối tượng Trip lấy ra từ cơ sở dữ liệu bằng phương thức Trip.list(). Thông thường phương thức này sẽ cho lại tất cả các bộ dữ liệu từ bảng. Dòng ngay ở trên nó ghi là: "Này, nếu bất kỳ ai đưa vào một thông số lớn nhất trong URL, hãy sử dụng nó để giới hạn số lượng các Trip quay trở lại. Nếu không, giới hạn số lượng Trip đến 10. Đường dẫn http://localhost:8080/trip-planner/trip/list sẽ tạo ra hoạt động này. Ví dụ: http://localhost:8080/trip-planner/trip/list?max=3 hiển thị 3 chuyến đi chứ không phải 10 như thường lệ. Nếu bạn muốn thể hiện nhiều chuyến đi, Grails tạo ra các kết nối tự động đánh số thứ tự trước và sau. Vậy thì sơ đồ chưa hoàn chỉnh (hashmap) này sẽ được sử dụng ở nơi nào? Hãy nhìn vào grails-app/views/list.gsp, ở Ví dụ 4: Ví dụ 4. list.gsp <g:each in="${tripList}" status="i" var="trip"> <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> <td> <g:link action="show" id="${trip.id}">${trip.id?.encodeAsHTML()}</g:link> </td> </tr> </g:each> list.gsp là một HTML cũ đơn giản với một ít GroovyTagLibs. Bất cứ cái gì đứng đầu g: là một GroovyTag. Trong ví dụ 4, <g:each> chuyển qua mỗi Chuyến đi trong tripList ArrayList và thiết kế nên một bản hoàn chỉnh HTML. Việc hiểu trình điều khiển (controllers) được tóm gọn thành ba chữ R: return (sự quay lại), redirect (sự chuyển hướng) và render (sự biểu hiện). Một vài hoạt động đã tận dụng được lợi thế của trạng thái quay lại ngầm định (implicit return statement) nhằm đưa lại dữ liệu vào một trang GSP với tên như cũ. Một số hoạt động sẽ định hướng lại cho bạn. Ví dụ để chuyển dữ liệu lại tới một trang GSP tên tượng tự. Một số hành động sẽ chuyển hướng bạn. Ví dụ, index (chỉ số) là hoạt động mà sẽ được hướng tới (get called) nếu bạn không xác định một hoạt động trong URL: def index = { redirect(action:list,params:params) } Trong trường hợp này TripController chuyển hướng bạn đến danh sách hoạt động, đi qua các tham số (hoặc QueryString) trong sơ đồ chưa hoàn chỉnh (hashmap) params. Cuối cùng là hoạt động lưu trữ (xem ví dụ 5) không có một trang lưu trữ .gsp tương ứng. Nó chuyển hướng bạn đến trang hoạt động trưng bày nếu như các bản ghi được lưu lại vào cơ sở dữ liệu mà không có lỗi. Nếu không nó sẽ hoàn trả lại trang create.gsp, tại đây bạn sẽ nhìn thấy các lỗi và có thể thử lại. Ví dụ 5. Hoạt động lưu trữ def save = { def trip = new Trip(params) if(!trip.hasErrors() && trip.save()) { flash.message = "Trip ${trip.id} created" redirect(action:show,id:trip.id) } else { render(view:'create',model:[trip:trip]) } } Nhưng không cần phải nói thêm về Grails hoạt động như thế nào, hãy xem nó trong quá trình hoạt động Ứng dụng trong hoạt động Gõ grails run-app tại dòng lệnh. Sau một loạt các thông điệp chạy qua màn hình, bạn sẽ nhận được một thông điệp ghi là: Server running. Browse to http://localhost:8080/trip-planner (Máy chủ đang chạy. Tìm tại http://localhost:8080/trip-planner) Nếu bạn đã có máy chủ chạy ở cổng 8080, bạn sẽ nhận được một kết xuất lõi kết thúc bằng : Server failed to start: java.net.BindException: Address already in use (Máy chủ không khởi động được: java.net.BindException: Địa chỉ đã bị sử dụng) Bạn có thể dễ dàng thay cổng mà Jetty chạy theo hai đường. Bạn có thể tạo ra một thay đổi tùy nghi bằng cách gõ grails -Dserver.port=9090 run-app. Để giữ cố định đối với thay đổi này thì nhìn vào $GRAILS_HOME/scripts/Init.groovy tìm dòng mà bắt đầu bằng serverPort và thay đổi giá trị serverPort = System.getProperty('server.port') ? System.getProperty('server.port').toInteger() : 9090 [...]... các ứng dụng lập kế hoạch chuyến đi (tripplanner) Cho đến lúc đó, hãy thưởng thức với Groovy và Grails Bạn sẽ không bao giờ nhìn vào việc phát triển trang web theo cùng một cách lại như vậy nữa Tải về Mô tả Mã ví dụ Tên j -grails0 1158.zip Thông tin về phương thức tải Kích thước 214KB Phương thức tải HTTP Mục lục  Sức mạnh của Groovy  Cài đặt Grails  Viết ứng dụng Grails đầu tiên của bạn  Ứng dụng. .. những lần khởi động máy chủ Nhấn cách thức của bạn để tới TripController và lưu thêm một số bản ghi Bạn sẽ không thể nhận thấy sự khác biệt nào giữa các hành vi của các ứng dụng Nếu biết rằng tất cả những thứ bạn nhìn thấy trong trình duyệt được điều khiển bằng 15 dòng mã, sẽ cho bạn một ý tưởng về sức mạnh của Grails Kết luận Tôi hy vọng bạn thích thú với cảm giác đầu tiên về Grails Một sức mạnh đáng... đã chạy Grails ở cổng mà bạn lựa chọn, đánh URL vào tìm kiếm web của bạn Bạn sẽ nhìn thấy một màn hình chào đón đưa ra một danh sách của tất cả các trình điều khiển của bạn, như trong Hình 1: Hình 1 Màn hình chào mừng của ứng dụng Grails Nhấn vào đường dẫn TripController Bạn sẽ có bản ứng dụng đầy đủ CRUD (tạo ra, đọc, cập nhật, xóa) ngay trong tầm tay để thao tác Tạo ra các chuyến đi mới sử dụng trang... quan ít hơn giải nén một tập tin Tạo một ứng dụng bằng cách gõ cặp lệnh từ vết cào (trên bề mặt gói) Tôi hy vọng lần giới thiệu thần tốc này sẽ kích thích ham muốn hơn nữa của bạn với Grails Chắc chắn nó tạo một bước tiến đối với bạn để phát triển dựa trên ví dụ này và đưa nó vào các hướng thú vị mới Trong lần cài đặt tháng tiếp theo, bạn sẽ mất nhiều thời gian với GORM Bạn sẽ lưu dữ liệu vào một cơ... này yêu cầu Grails thực hiện tất cả những thứ đã được làm với trình điều khiển trước đây, chỉ để tạo ra tất cả những hành động danh sách (list), lưu trữ (save), và sửa đổi (edit) trong bộ nhớ hoạt động tại thời gian chạy Ba dòng mã thay vì 66 sản xuất ra tất cả cho cùng một hành vi Gõ lại grails run-app —Tất cả dữ liệu của bạn đã mất Đừng hoảng sợ Nhấn CtrlC để đóng Grails Lần này, gõ grails prod run-app... được thể hiện ở hình 2: Hình 2 Tạo lập trang các chuyến đi Hiệu chỉnh các chuyến đi của bạn bằng cách sử dụng trang ở hình 3: Hình 3 Trang danh sách các chuyến đi Và mất bao nhiêu lâu để bạn đưa các ứng dụng lên và chạy? Có bao nhiêu dòng mã? Đây là cách bạn tìm hiểu: 1 Nhấn Ctrl-C để đóng Grails 2 Gõ grails stats Bạn sẽ thấy hiển thị trên màn hình: + + -+ -+ | Name | Files | LOC | + ... Mất ít hơn 100 dòng mã để thực hiện tất cả các chức năng của ứng dụng Không tồi chút nào Nhưng cho phép tôi trình bày thêm một gợi ý trước khi kết thúc Tạo ra các trình điều khiển (controller) và khung nhìn (views) là một bài tập tuyệt vời, và có các tệp vật lý trên đĩa giúp minh họa tất cả mọi thứ liên kết với nhau như thế nào Tuy nhiên, làm cho tôi một việc: xóa các nội dung của lớp TripController . đăng báo đầu tiên của loạt bài hàng tháng Làm chủ Grails - Mastering Grails, chuyên gia về Java Scott Davis đưa ra Grails và trình diễn làm thể nào để thiết kế ứng dụng Grails đầu tiên của. cảm hứng, chứ không được chuyển dịch. Để bắt đầu loạt bài Làm chủ Grails, bài viết này giới thiệu bạn với nền Grails, cho bạn thấy làm thế nào để cài đặt nó và qua việc xây dựng ứng dụng Grails. Làm chủ Grails: Xây dựng ứng dụng đầu tiên với Grails Hãy xem sức mạnh lớn thế nào trong một bộ khung web nhỏ bé này Scott

Ngày đăng: 07/08/2014, 10:22

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan