Giới thiệu thư viện Apache Commons Chain
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
1. Apache Commons Chain là gì?
Apache Commons Chain là một framework, một thư viện mã nguồn mở của Apache. Nó cung cấp API cho phép chúng ta dễ dàng cài đặt các xử lý tuân theo Chain of Responsibility (COR) pattern.
Chain of Responsiblity cho phép một đối tượng gửi một yêu cầu nhưng không biết đối tượng nào sẽ nhận và xử lý nó. Điều này được thực hiện bằng cách kết nối các đối tượng nhận yêu cầu thành một chuỗi (chain) và gửi yêu cầu theo chuỗi đó cho đến khi có một đối tượng xử lý nó.
Chain of Responsibility Pattern hoạt động như một danh sách liên kết (Linked list) với việc đệ quy duyệt qua các phần tử (recursive traversal).
2. Cài đặt thư viện Apache Commons Chain
Thêm thư viện vào project vào project maven:
<!-- https://mvnrepository.com/artifact/commons-chain/commons-chain --> <dependency> <groupId>commons-chain</groupId> <artifactId>commons-chain</artifactId> <version>1.2</version> </dependency>
3. Sử dụng thư viện Apache Commons Chain
Trong Chain framework, các đơn vị công việc trong một chuỗi các bước tuần tự được biểu diễn bằng các Command. Một chuỗi các lệnh (Command) tạo thành một chain. Chuỗi chính nó cũng là một Command có thể được thực thi. Chain được giữ trong danh mục (Catalog), từ đó chúng có thể được lấy tại thời điểm thực thi. Chain framework cung cấp các interface Chain, Catalog, Command, Fillter như sơ đồ lớp bên dưới.
- Context : thể hiện trạng thái của ứng dụng, chứa dữ liệu được chia sẻ giữa các Command.
- Command : thể hiện các đơn vị công việc. Command chỉ bao gồm một phương thức execute() với tham số là một Context và kiểu trả về là giá trị boolean. Kiểu trả về boolean: để xác định có nên tiếp tục xử lý hay không. Trả về true nếu đã hoàn thành, không cần Command kế tiếp xử lý, ngược lại trả về false.
- Chain : nó extends từ Command. Chain có thể chứa một chuỗi tuần tự các Command/ Chain khác.
- Filter : là một Command đặc biệt, có thêm phương thức postProcess(). Phương thức này được gọi sau khi phương thức execute() được gọi.
- Catalog : các ứng dụng sử dụng Facade hoặc Factory hoặc DI để giảm sự phụ thuộc giữa các lớp. Các lớp có thể giao tiếp với nhau mà không cần thông qua tên lớp. Catalog cũng dựa trên ý tưởng đó, nó cho phép client có thể thực thi một Command thông qua định danh mà không cần biết tên lớp.
3.1 Ví dụ sử dụng thư viện Apache Commons Chain
Tạo custom context để chia sẻ dữ liệu giữa các Command.
package com.gpcoder.context; import org.apache.commons.chain.impl.ContextBase; import lombok.Data; @Data public class MyContext extends ContextBase { private String property; }
Tạo file constant đặt tên cho các command, chain. Chúng ta sẽ sử dụng tên này để lấy các Command từ Catalog và thực thi.
package com.gpcoder.constant; public enum MyCommandNamed { CHAIN_1, CHAIN_2, CMD_1, CMD_2, CMD_3, CMD_4, EXCEPTION_HANDLER }
Tạo file constant đặt tên cho các command, chain. Chúng ta sẽ sử dụng tên này để lấy các Command từ Catalog và thực thi.
package com.gpcoder.constant; public enum MyCommandNamed { CHAIN_1, CHAIN_2, CMD_1, CMD_2, CMD_3, CMD_4, EXCEPTION_HANDLER }
Tạo Command1, Command2: đây là command minh họa cho đơn vị công việc sẽ được thực hiện trong Chain.
package com.gpcoder.command; import org.apache.commons.chain.Command; import org.apache.commons.chain.Context; import com.gpcoder.context.MyContext; public class Command1 implements Command { public boolean execute(Context ctx) throws Exception { System.out.println("This is command 1: "); String property = ((MyContext) ctx).getProperty(); System.out.println("+ property: " + property); String customValue = ctx.get("custom-key").toString(); System.out.println("+ customValue: " + customValue); return false; } }
package com.gpcoder.command; import org.apache.commons.chain.Command; import org.apache.commons.chain.Context; import com.gpcoder.context.MyContext; public class Command2 implements Command { public boolean execute(Context ctx) throws Exception { String value = ((MyContext) ctx).getProperty(); System.out.println("This is command 2: " + value); return false; } }
Tham khảo việc làm Java Fresher mới nhất trên TopDev!
3.1.1 Ví dụ 1 – Thực thi 1 Chain
Tạo chain có 2 command: Command1 và Command2.
package com.gpcoder.example1_chain; import org.apache.commons.chain.impl.ChainBase; import com.gpcoder.command.Command1; import com.gpcoder.command.Command2; public class MyChain extends ChainBase { public MyChain() { super(); addCommand(new Command1()); addCommand(new Command2()); } }
Thực thi chain:
package com.gpcoder.example1_chain; import org.apache.commons.chain.Command; import com.gpcoder.context.MyContext; /** * Execute a chain example */ public class ChainStart1 { public static void main(String[] args) throws Exception { // Create context MyContext context = new MyContext(); context.setProperty("property-value"); context.put("custom-key", "custom-value"); // Get the command Command command = new MyChain(); command.execute(context); } }
Output chương trình:
This is command 1: + property: property-value + customValue: custom-value This is command 2: property-value
3.1.2 Ví dụ 2 – Thực thi một Command được lấy từ Catalog
Tạo Catalog chứa các command và Chain cần sử dụng:
package com.gpcoder.example2_catalog; import org.apache.commons.chain.impl.CatalogBase; import com.gpcoder.command.Command1; import com.gpcoder.command.Command2; import com.gpcoder.constant.MyCommandNamed; import com.gpcoder.example1_chain.MyChain; /** * CatalogBase is a collection of Chains and Commands with their logical names. */ public class MyCatalog extends CatalogBase { public MyCatalog() { super(); addCommand(MyCommandNamed.CMD_1.name(), new Command1()); addCommand(MyCommandNamed.CMD_2.name(), new Command2()); addCommand(MyCommandNamed.CHAIN_1.name(), new MyChain()); } }
Lấy Command từ Catalog và thực thi:
package com.gpcoder.example2_catalog; import org.apache.commons.chain.Catalog; import org.apache.commons.chain.Command; import com.gpcoder.constant.MyCommandNamed; import com.gpcoder.context.MyContext; /** * Execute the specific command example */ public class ChainStart2 { public static void main(String[] args) throws Exception { // Create context MyContext ctx = new MyContext(); ctx.setProperty("property-value"); ctx.put("custom-key", "custom-value"); // Get the catalog Catalog catalog = new MyCatalog(); // Get the command System.out.println("Execute the specific command: CMD_1"); Command command1 = catalog.getCommand(MyCommandNamed.CMD_1.name()); command1.execute(ctx); System.out.println("\nExecute the specific chain: CHAIN_1"); Command chain1 = catalog.getCommand(MyCommandNamed.CHAIN_1.name()); chain1.execute(ctx); } }
Output chương trình:
Execute the specific command: CMD_1 This is command 1: + property: property-value + customValue: custom-value Execute the specific chain: CHAIN_1 This is command 1: + property: property-value + customValue: custom-value This is command 2: property-value
3.1.3 Ví dụ 3 – Sử dụng Filter để xử lý exception
Tạo Command3 có throw một Exception:
package com.gpcoder.command; import org.apache.commons.chain.Command; import org.apache.commons.chain.Context; public class Command3 implements Command { public boolean execute(Context ctx) throws Exception { throw new UnsupportedOperationException("Cannot execute Command4"); } }
Tạo Fitler để handle Exception:
package com.gpcoder.filter; import org.apache.commons.chain.Context; import org.apache.commons.chain.Filter; public class CommandExceptionHandler implements Filter { public boolean execute(Context context) throws Exception { System.out.println("CommandExceptionHandler.execute() called."); return false; } public boolean postprocess(Context context, Exception exception) { if (exception == null) { return false; } System.out.println("Exception " + exception.getMessage() + " occurred."); return true; } }
Tạo Chain với 2 Command: Command1 và Command3.
package com.gpcoder.example3_exception; import org.apache.commons.chain.impl.ChainBase; import com.gpcoder.command.Command1; import com.gpcoder.command.Command3; import com.gpcoder.filter.CommandExceptionHandler; public class MyChain3 extends ChainBase { public MyChain3() { super(); addCommand(new CommandExceptionHandler()); addCommand(new Command1()); addCommand(new Command3()); } }
Thực thi chain:
package com.gpcoder.example3_exception; import org.apache.commons.chain.Command; import com.gpcoder.context.MyContext; /** * Filter for exception handling in Chains example */ public class ChainStart3 { public static void main(String[] args) throws Exception { Command chain = new MyChain3(); chain.execute(new MyContext()); } }
Output của chương trình:
CommandExceptionHandler.execute() called. This is command 1: + property: null Exception null occurred.
Lưu ý: Filter Command phải thêm vào đầu chain để có thể handle exception cho các Command được thực thi phía sau.
3.1.4 Ví dụ 4 – Tạo Catalog từ file xml
Trong ví dụ 2, chúng ta tạo một Catalog bằng cách extend từ một CatalogBase. Chúng ta có một cách khác để tạo Catalog là xử dụng cấu hình catalog từ xml file.
Để sử dụng tính năng này, cần thêm các thư viện sau:
<!-- Use for the command config --> <!-- https://mvnrepository.com/artifact/commons-digester/commons-digester --> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>1.8</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.3</version> </dependency>
Để dễ minh họa các trường hợp sử dụng cấu hình xml file, chúng ta sẽ tạo thêm một Command:
package com.gpcoder.command; import org.apache.commons.chain.Command; import org.apache.commons.chain.Context; import com.gpcoder.context.MyContext; public class Command4 implements Command { public boolean execute(Context ctx) throws Exception { String value = ((MyContext) ctx).getProperty(); System.out.println("This is command 4: " + value); return false; } }
Tạo file chain-config.xml trong thư mục /src/main/resources
<catalog> <!-- Single command "chains" from CatalogBaseTestCase --> <command name="CMD_1" className="com.gpcoder.command.Command1" /> <command name="CMD_2" className="com.gpcoder.command.Command2" /> <!-- Chains with nested commands --> <chain name="CHAIN_1"> <command className="com.gpcoder.filter.CommandExceptionHandler" /> <command className="com.gpcoder.command.Command1" /> <command className="com.gpcoder.command.Command2" /> <command className="com.gpcoder.command.Command3" /> <!-- Lookup Command --> <command name="CHAIN_2" optional="true" className="org.apache.commons.chain.generic.LookupCommand" /> </chain> <chain name="CHAIN_2"> <command className="com.gpcoder.command.Command4" /> </chain> </catalog>
Tạo Catalog từ XML file:
package com.gpcoder.example4_xml_config; import org.apache.commons.chain.Catalog; import org.apache.commons.chain.config.ConfigParser; import org.apache.commons.chain.impl.CatalogFactoryBase; /** * Get catalog from the XML configuration file */ public class MyXMLCatalog { private static final String CONFIG_FILE = "/chain-config.xml"; private ConfigParser parser; private Catalog catalog; public MyXMLCatalog() { parser = new ConfigParser(); } public Catalog getCatalog() throws Exception { if (catalog == null) { parser.parse(this.getClass().getResource(CONFIG_FILE)); } catalog = CatalogFactoryBase.getInstance().getCatalog(); return catalog; } }
Thực thi Command lấy từ Catalog trên:
package com.gpcoder.example4_xml_config; import org.apache.commons.chain.Catalog; import org.apache.commons.chain.Command; import com.gpcoder.constant.MyCommandNamed; import com.gpcoder.context.MyContext; /** * Get the catalog from XML file and Execute the specific command example */ public class ChainStart4 { public static void main(String[] args) throws Exception { // Create context MyContext ctx = new MyContext(); ctx.setProperty("property-value"); ctx.put("custom-key", "custom-value"); // Get the catalog Catalog catalog = new MyXMLCatalog().getCatalog(); // Get the command System.out.println("Execute the specific command: CMD_1"); Command command1 = catalog.getCommand(MyCommandNamed.CMD_1.name()); command1.execute(ctx); System.out.println("\nExecute the specific chain: CHAIN_1"); Command chain1 = catalog.getCommand(MyCommandNamed.CHAIN_1.name()); chain1.execute(ctx); } }
Output chương trình:
Execute the specific command: CMD_1 This is command 1: + property: property-value + customValue: custom-value Execute the specific chain: CHAIN_1 CommandExceptionHandler.execute() called. This is command 1: + property: property-value + customValue: custom-value This is command 2: property-value Exception Cannot execute Command4 occurred.
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- F Framework nào tốt nhất cho dự án của bạn? – Checklist chi tiết
- K Kinh nghiệm xử lý responsive table hiệu quả
- S Stackoverflow là gì? Bí kíp tận dụng Stack Overflow hiệu quả
- 7 7 kinh nghiệm hữu ích khi làm việc với GIT trong dự án
- B Bài tập Python từ cơ bản đến nâng cao (có lời giải)
- B Bảo mật API là gì? Một số nguyên tắc và kỹ thuật cần biết
- H Hướng dẫn cài đặt và tự học lập trình Python cơ bản từ A-Z
- C Chinh Phục Phân Tích Dữ Liệu Với Pandas Trong Python: Hướng Dẫn Từng Bước
- D Display CSS là gì? Cách khai báo và sử dụng thuộc tính display trong CSS