1. 4 Cấu trúc khóa luận
3.5. Một số khái niệm cơ bản trong AspectJ:
3.5.1. Join point
Join point là bất kỳ điểm nào có thể xác định được khi thực hiện chương trình. Ví
dụ: lời gọi hàm, khởi tạo đối tượng. Join point chính là vị trí mà các hành động thực thi cắt ngang được đan vào. Trong AspectJ mọi thứ đều xoay quanh join point.
Một số loại join point chính trong AspectJ:
oJoin point tại hàm khởi tạo (constructor).
oJoin point tại các phương thức.
oJoin point tại các điểm truy cập thuộc tính.
oJoin point tại các điểm điều khiển ngoại lệ: Được điều khiển trong khối điều khiển
ngoại lệ.
3.5.2. Pointcut
Pointcut là một cấu trúc chương trình mà nó chọn các join point và ngữ cảnh tạicác
join point đó [7, 12]. Ví dụ một pointcut có thể chọn một join point là lời gọi đến một phương thức và lấy thông tin ngữ cảnh của phương thức đó như đối tượng chứa phương thức đó, các tham số của phương thức đó.
Cú pháp của pointcut được khai báo như sau:
[access specifier] pointcut pointcut-name([args]) : pointcut-definition;
Ví dụ:
public pointcut test(): call(void Line.setP1(Point));
3.5.3. Advice
Advice là mã thực hiện tại một join point mà được chọn bởi pointcut [7, 12]. Haynói
cách khác, nếu ta coi pointcut là khai báo tên phương thức, thì advice là phần thân của phương thức đó. Pointcut và advice sẽ hình thành nên các luật đan kết các quan hệ
đan xen.
Advice được chia ra làm ba loại sau:
1. Before: Được thực hiện trước join point.
2. After: Được thực hiện sau join point.
3. Around: Thực thi xung quanh join point.
pointcut updateDisplay(): execution(void *.moveBy(int,int)) Ta có thể xây dựng các advice như sau:
- Before advice thự hiện lưu vết before() : updateDisplay() { // logging
}
- After advice thực hiện cập nhật hình after() : updateDisplay() { display.update(this);
}
Ví dụ về around advice dùng để kiểm tra thuộc tính age của lớp Person trong phương thức setAge() có vi phạm điều khiện không (điều kiện: age > 0).
void around(Person person, int age):setAge(person, age) { if(age > 0) Process(person,age); else System.out.println("Invalid Age!"); } 3.5.4. Aspect
Aspect là phần tửtrung tâm của AspectJ, giống như class trong Java. Aspect chứa mã
thể hiện các luật đan kết cho các concern. Join point, pointcut, advice được kết hợp trong
aspect .
Aspect được khai báo theo mẫu sau:
[access specification] aspect <AspectName> [extends class-or-aspect-name] [implements interface-list] [<association-specifier>(Pointcut)] { ... aspect body } Ví dụ:
public abstract aspect AbstractLogging {
public abstract pointcut logPoints(); public abstract Loger getLogger();
before():logPoints() {
35
getLogger().log(Level.INFO, “Before” + thisJoinPoint); }
}
Tuy có gần giống các đặc điểm của class trong Java như: chứa thuộc tính, phương thức, có thể khai báo trừu tượng, có thể kế thừa… Tuy nhiên, Aspect có một số khác biệt cơ bản sau:
1. Aspect không thể khởi tạo trực tiếp.
2. Aspect không thể khởi tạo trực tiếp.
3. Aspect không thể kếthừa từmột aspect cụ thể(không phải trừu tượng)
Aspect có thể được đánh dấu là có quyền bằng định danh privileged. Nhờ đó nó có
thể truy cập đến các thành viên của lớp mà chúng cắt ngang.
3.6. Cơ chế họa động của AspectJ
Aspect compiler là thành phần trung tâm của AspectJ, nó có nhiệm vụ kết hợp các file mã nguồn Java với các aspect lại với nhau để tạo ra chương trình cu ối cùng. Quá trình dệt có thể xảy ra ở các thời điểm khác nhau: compile – time, link – time và load – time.
3.6.1. Compile – time:
Dệt trong thời gian biên dịch là cách đơn giản nhất. Mã nguồn Java và các aspect sẽ được kết hợp với nhau trước khi trình biên dịch dịch mã nguồn ra dạng byte code. Hay nói cách khác, trước khi biên dịch, các mã aspect sẽ được phân tích, chuyển đổi sang dạng mã Java và được chèn chính xác vào các vị trí đã định nghĩa sẵn trong mã nguồn Java chính của chương trình. Sau đó trình biên dịch sẽ dịch mã đã được dệt này ra dạng byte code. AspectJ 1.0.x sử dụng cách này để dệt chương trình.
3.6.2. Link – time:
Quá trình dệt được thực hiện sau khi mã nguồn Java và các aspect được biên dịch ra
dạng byte code. Một bộ xử lý tĩnh được sửdụng để đánh dấu các điểm gọi hàm trong mã
được viết bằng java. Khi một hàm được thực thi. Runtime system sẽ phát hiện ra điểm nào cần gọi đến mã aspect để thực thi và khi đó mã aspect sẽ được gọi để đan vào chương trìnhchính. AspectJ 1.1.x sử dụng cách này để dệt chương trình.
3.6.3. Load – time:
Quá trình dệt được thực hiện khi máy ảo Java tải một class vào để chạy. Theo cách này, mã nguồn Java và các aspect được biên dịch ra dạng byte code. Quá trình dệt diễn ra
3.7. Sử dụng AOP Phát triển ứng dụng và phương pháp kiểm chứng dựa trên AOP dựa trên AOP
Ngày nay, AOP được ứng dụng rộng rãi trong việc phát triển phần mềm. Phát triển hệ thống sử dụng AOP tương tự như phát triển hệ thống sử dụng các phương pháp khác, cũng g ồm các bước như: xác định concern, cài đặt concern và kết hợp chúng lại tạo thành hệ thống cuối cùng. Cộng đồng nghiên cứu AOP đề xuất ba bước thực hiện như sau:
o Phân tích bài toán theo khía cạnh (Aspectual decomposition): Trong bước này chúng ta phân tích các yêu cầu nhằm xác định các chức năng chính của hệ thống và các chức năng cắt ngang hệ thống. Các phương thức cắt ngang hệ thống được tách ra khỏi các chức năng chính.
o Xây dựng các chức năng (Concern Implementation): Cài đặt các chức năng một cách độc lập.
o Kết hợp các khía cạnh lại để tạo nên hệ thống hoàn chỉnh (Aspectual Recompositon): Trong bước này chúng ta chỉra các quy luật kết hợp bằngcách tạo ra các
aspect. Quá trình này gọi là quá trình dệt mã, sử dụng các thông tin trong aspect để cấu
thành hệ thống cuối cùng.
Hình 12. Các giai đoạn phát triển ứng dụng sử dụng AOP
AOP đã được nghiên cứu và áp dụng vào rất nhiều ngôn ngữ lập trình như: Java, C, C#, PHP. Một số dự án được liệt kê trong bảng dưới đây:
Bảng: Các dự án nghiên cứu AOP
Tên dự án Địa chỉ
AspectJ http://www.eclipse.org/aspectj/
AspectWerkz http://aspectwerkz.codehaus.org/
Jboss AOP http://jboss.org/
Sping AOP http://www.springsource.org/
37
AspectC++ http://aspectc.org/
JAC http://jac.ow2.org/
Từ khả năng mô-đun hóa các quan hệ đan xen, các chức năng cắt ngang hệ thống; tách rời sự hoạt động của các mô-đun cũng như nhiều ưu điểm khác của AOP so với OOP mà hiện nay AOP đã trở thành sự lựa chọn phù hợp cho rất nhiều hệ thống phần mềm; đặc biệt là trong các chức năng lưu vết, bảo mật, xác thực của hệ thống phần mềm. Ngoài ra, do các mã aspect độc lập với mã nguồn chính của chương trình, có thể sửa đổi tùy theo ý muốn của lập trình viên, chính vì vậy AOP cònđư ợc ứng dụng rất lớn vào các loại kiểm chứng trong quá trình thiết kế phần mềm. Ví dụ như: kiểm chứng giao thức [5], kiểm tra việc gọi tuần tự các hàm trong chương trình…
Nội dung chính của các phương pháp kiểm chứng dựa trên AOP là dựa vào những kiến khái niệm cơ bản của AOP như: join point, pointcut, advice, aspect để xây dựng nên các mô-đun kiểm chứng (các aspect) từ các chức năng cắt ngang hệ thống. Các aspect này sẽ được đan vào khung mã ngu ồn chương trình thông qua trình biên dịch AspectJ để thực hiện chức năng kiểm chứng.
CHƯƠNG 4.
KIỂM CHỨNG CÁC GIAO THỨC BẰNG AOP
4.1 Biểu diễn giao thức.
Một giao thức trên các đối tượng là việc gọi tuần tự các phương thức trong biểu đồ trình tự theo quy tắc được chỉ ra từ trước. Trong phạm vi khóa luận này, tôi chỉ diễn tả cách biểu diễn giao thức các giao thức cơ bản như ABn AnBm, (A*B)n. Trong đó, A,B là các giao thức còn m,n là số lần A,B được gọi trong chương trình. [5] đã kiểm chứng được các giao thức (AB)n, (A*B)n trên biểu đồ trình tự, xong chưa xử lý với precondition và postcondition. Trong phạm vi bài khóa luận tôi sẽ trình bày việc xử lý các giao thức được định nghĩa ở trên biểu đồ tuần tự, bên cạnh đó sử lý triệt để hơn các ràng buộc precondition và postcondition cho tất cả các giao thức. Trong bài khóa luận, tôi đề cập hai cách để biểu diễn giao thức:
- Biểu diễn trên biểu đồ tuần tự: Bằng cách sử dụng hai vòng lặp, là một thành phần trong combindedFragment để miêu tả giao thức trên. Bằng cách mô tả này, ta có thể mô tả hầu hết tất cả các giao thức cơ bản. Trong ví dụ dưới đây, một vòng lặp thể hiện việc gọi m lần giao thức Message2 và một vòng lặp thể hiện gọi n lần giao thức
Message4.
39
- Biểu diễn giao thức bằng biểu thức : Việc biểu diễn bằng biểu thức giúp cho việc thực hiện quá trình kiểm tra giao thức một cách nhanh chóng. Giao thức AnBm được biểu diễn bằng biểu thức [A]n[B]m.
Ví dụ : [void message2()]m [void message4()]n như trong hình vẽ. Như trong định nghĩa trên thì phương thức void message2() sẽ được gọi m lần và phương thức void message4() sẽ được gọi n lần trong mã nguồn chương trình.
4.2. Tiền điều kiện và hậu điều kiện : 4.2.1. Tiền điều kiện : 4.2.1. Tiền điều kiện :
Tiền điều kiện biểu diễn những ràng buộc mà một thủ tục sẽ thực hiện một cách chính xác. Ví dụ như :
- Put sẽ không được gọi nếu ngăn xếp đã đầy.
- Remove item sẽ không được thực hiện trên một ngăn xếp khi ngăn xếp là rỗng. Tiền điều kiện vào có hiệu lực đến tất cả những lời gọi thủ tục, cả trong lớp và từ những lớp liên quan. Một hệ thống chính xác sẽ không bao giờ thực thi một lời gọi không thỏa mãn tiền điều kiện.
4.2.2. Hậu điều kiện :
Hậu điều kiện biểu diễn những thuộc tính của trạng thái kết quả có được sau khi thực hiện thủ tục. Ví dụ như :
- Sau thủ tục put, ngăn xếp sẽ không thể rỗng, phần tử trên cùng là phần tử mời được thêm vào và số lượng phần tử sẽ tăng lên 1.
- Sau khi remove, ngăn xếp không thể đầy, số phần tử giảm đi 1.
Sự có mặt của mệnh đề hậu điều kiện trong thủ tục bảo đảm kết quả của thủ tục sẽ thỏa mãn các thuộc tính(giả sử rằng thủ tục đã thỏa mãn tiền điều kiện để được gọi).
4.2.3 Biểu diễn tiền điều kiện và hậu điều kiện trong biểu đồ trình tự :
Trong sequence diagram, để biểu diễn tiền điều kiện và hậu điều kiện tôi dùng một thành phần Comment để biểu diễn, Comment này sẽ chứa đựng thông tin về loại điều kiện (tiền điều kiện hoặc hậu điều kiện), điều kiện ràng buộc và thủ tục liên quan. Ví dụ như ở trong hình vẽ:
- Comment thứ nhất có nội dung: pre:x=0:Message2 biểu diễn rằng kiểu của điều kiện là tiền điều kiện(pre), điều kiện ở đây là x=0 và phương thức liên quan đến là message2.
- Comment thứ nhất có nội dung: pos:x>0:Message2 biểu diễn rằng kiểu của điều kiện là hậu điều kiện(pos), điều kiện ở đây là x>0 và phương thức liên quan đến là message2.
Hình 14. Mô tả tiền điều kiện và hậu điều kiện trong biểu đồ tuần tự
4.3 Kiểm chứng giao thức:
Giả thiết cần kiểm chứng biểu đồ như trong hình 9.Việc kiểm chứng giao thức sẽ làm được những việc sau :
- Kiểm tra xem mã nguồn chương trình có thực hiện tuần tự các phương thức như trong thiết kế của biểu đồ trình tự hay không: Từ thiết kế trên, khi ta viết mã chương trình thì chương trình phải tuân thủ các nguyên tắc trong bản thiết kế trên như: phương thức void message1() phải được gọi trước phương thức void message2(), phương thức void message2() gọi trước phương thức void message4(). Trước void message2() thì phải gọi void message1() trước…
Để giải quyết vấn đề này bằng AOP ta đặt tên cho các trạng thái của chương trình khi chương trình thực thi bằng mã aspect. Ban đầu là trạng thái mở đầu(state_start), sau khi gọi 1 phương thức thì trạng thái sẽ bị thay đổi và được đánh dấu ở phương thức đó. Sau cùng là trạng thái kết thúc.
- Kiểm chứng xem mã nguồn có đáp ứng các ràng buộc nếu có không : Là việc xác minh các tiền điều kiện và hậu điều kiện được miêu tả trong biểu đồ tuần tự là đã được xác minh hay chưa. Trong hình 9 mô tả ràng buộc với phương thức void message2(). Trước khi gọi void message2() thì kiểm tra x=0 có đúng hay không. Sau khi gọi void message2() thì x>0 có đúng hay không. Việc kiểm chứng các ràng buộc như vậy là không khó khăn chỉ có điều khi ràng buộc thể hiện một cách phức tạp thì chúng ta phải viết tách các điều kiện ra thành từng điều kiện nhỏ để có thể kiểm chứng.
- Thông báo lỗi và chỉ ra lỗi trong đoạn cài đặt của chương trình: Khi có lỗi cài đặt được phát hiện thì mã kiểm chứng cho in ra dòng thông báo lỗi ở vị trí liên quan để người kiểm tra có thể dễ dàng sửa lại cho đúng.
41
Trường hợp Sequence diagram định nghĩa các combindedFragment thì việc kiểm chứng sẽ xác minh cả các combindedFragment mà có chứa điều kiện có được thực thi đúng không. Ví dụ như Loop là một combindedFragment, chương trình sẽ kiểm tra xem Loop có được thực hiện đúng không, về số vòng lặp, giao thức lặp….
Trong trường hợp không định nghĩa một CombindedFragment nào trong biểu đồ tuần tự thì việc kiểm chứng giao thức sẽ là kiểm chứng cài đặt trên sequence diagram. Tức là sẽ kiểm tra đơn thuần mã thực thi có thực hiện tuần tự như trong thiết kế biểu đồ tuần tự hay không.
Duyệt các trạng thái : Các trạng thái được đặt vào Enumeration và được duyệt lần lượt để đặt các trạng thái vào sau khi phương thức được gọi.
Chương 5 Xây dựng công cụ sinh mã từ máy trạng thái 5.1 Tổng quan về xây dựng công cụ sinh mã từ máy trạng thái.
Dựa vào cấu trúc của mã aspect tôi đã trình bày ở phần trên, tôi tiến hành phương pháp cài đặt mã aspect từ máy trạng thái. Mã aspect kiểm thử gồm các pointcut Việc cài đặt mã aspect bao gồm những việc sau đây :
- Lấy các thành phần cần sử dụng trong tài liệu XMI . - Duyệt các trạng thái từ FSM.
- Xác định số pointcut cần đặt vào. Số pointcut cần thiết bằng số lượng phương thức trong chương trình.
- Xác định các hành vi mà hệ thống cần thực thi, những điều kiện ràng buộc cho các giao thức. Trước khi gọi phương thức thì trạng thái của hệ thống phải ra sao, có những điều kiện gì đi kèm, sau khi gọi phương thức thì điều kiện ra sao. Trong bước này ta xác định các mã kiểm chứng Advice.
Sau khi xác định được các pointcut và advice, tổng hợp mã trên theo cấu trúc cú pháp của AspectJ.
5.1.1 Lấy các thành phần trong tài liệu XMI.
Truy xuất các đường đời (Lifeline)
Một đường đời trong tệp XMI được biểu diễn bởi phần tử có thẻ là <lifeline> trong đó có nhiều thuộc tính, ta chỉ quan tầm tới hai thuộc tính là id và name. Ta sẽ lưu hai thuộc tính này trong đó name thì ta chỉ quan tâm tới tên lớp (nhớ lại quy tắc tên một đường đời là objectName:ClassName ) tức là sau dấu hai chấm còn bỏ đi tên đối tượng.
Vì trong một biểu đồ có thể có nhiều đường đời có cùng tên lớp nhưng khác nhau tên đối tượng do vậy, mỗi khi ta truy xuất thêm một đường đời mới ta lại phải kiểm tra xem trước đó đã có đường đời nào cùng lớp với nó hay chưa nếu có rồi thì tên đối tượng sẽ được đặt theo quy tắc sau: object1, object2, …
Sau khi lưu hết các phần tử này, kết quả ta sẽ có được một dãy các đường đời đã vẽ trên biểu đồ.
Truy xuất các thông điệp (Message)
Một thông điệp được biểu diễn bởi phần tử có thẻ là <message> trong đó ta quan tâm tới thuộc tính id, name, sendEvent, receiveEvent và messageSort. Tên của thuộc tính ta sẽ chỉ lấy lại tên của phương thức còn bỏ đi phần tham số. Từ hai thuộc tính sendEvent
43
và receiveEvent, thông qua phần tử <fragment> mà có thuộc tính xmi:type= “uml:MessageOccurrenceSpecification ta sẽ tìm ra đường đời nguồn và đích của thông điệp đấy. Từ thuộc tính messageSort ta sẽ biết được thông điệp đấy thuộc loại nào trong bảy loại thông điệp đã nêu trên.
Kết quả ta sẽ thu được một dãy các thông điệp. Tuy nhiên có một vấn đề là thứ tự