1 / 25

Leveraging the Interceptor Pattern for Software Development Efficiency

Learn how the Interceptor architectural pattern enhances modularity, flexibility, and extensibility in software development. Understand its implementation, benefits, and use cases.

vevans
Download Presentation

Leveraging the Interceptor Pattern for Software Development Efficiency

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. E81 CSE 532S: Advanced Multi-Paradigm Software Development Interceptor Pattern Venkita Subramonian, Christopher Gill, Morgan Deters, Anand Krishnan Department of Computer Science and Engineering Washington University, St. Louis cdgill@cse.wustl.edu

  2. Overview • Abstraction • Genericity • Conformity • Extensibility The Interceptor architectural pattern allows services to be added transparently to a framework and triggered automatically when certain events occur. • Reusability • Modularity • Flexibility

  3. Motivation int send_data(Buffer *buf, size_t n) { log(buf, n); if(!check_security(buf, n) return 0; encrypt(buf, n); // etc... finally_actually_send_data(buf, n); return 1; } • Simple client/server • add logging features • add security checks • add encryption • add QoS/prioritization • add special-case or exception handling • E.g., traffic overload, administrator access, etc. • Want to add new processing for existing events • Code easily becomes tangled and unmanageable • Want to add new processing for new events • Adding a new interception point is error-prone

  4. Problem • Fundamental concern • How to integrate out-of-band tasks with in-band tasks? • Straightforward (and all to common) approach • Paste the out-of-band logic wherever it’s needed • May be multiple places to paste it • Brittle, tedious, error-prone, time/space (e.g., inlining) • Is there a better and more general approach? Out-of-Band (Admin) In-Band (Client) In-Band (Server)

  5. Interceptor Solution Approach • Our goal is to find a general mechanism to integrate out-of-band tasks with in-band tasks • In-band tasks • Processed as usual via framework • Out-of-band tasks • register with framework via special interfaces • are triggered by framework on certain events • Some events are generated by in-band processing • are provided access to framework internals (i.e., context) via specific interfaces

  6. Interceptor Interaction diagram Application Concrete Framework Concrete Interceptor Dispatcher create create attach Concrete Interceptor event create Context Object callback iterate_list Context object callback get_value access_internals

  7. Interceptor Pattern Structure

  8. Implementation • Model internal behavior of concrete framework • Identify and model interception points • Identify concrete framework state transitions • Partition interception points into reader/writer sets • Integrate them into state machine model • Partition them into disjoint interception groups • Specify the context object • semantics • number of context object types • strategy for passing context objects to interceptors • per-registration vs. per-event

  9. Implementation (continued) • Specify the interceptors • Specify the dispatchers • interceptor registration interface • dispatcher callback interface • Implement call-back mechanisms in concrete framework • Implement concrete interceptors

  10. Example Pattern Language Sketch • Data streams need transparent security protection • Apply an Interceptor to encode and decode stream (insert an object into a path of the system) • Smart adversary might learn en/decoding in use • Apply ACT to identify new en/decoding schemes at run-time • Triggering changes may complicate threading • Apply Reactor for in-and-out-of-band processing • Need to trigger changes on remote applications • Apply Acceptor/Connector to set up connection to a remote interceptor dispatcher • Any point in the code might detect a need to change • Apply Singleton to interceptor dispatcher • Make these dispatchers remote Observers of one another

  11. Design Outline • Multi-threaded and/or reactive concurrency with streams of text (as in lab assignments) • Add encode/decode interceptor capabilities • XOR, +k, -k, *k, /k, rotate(k), IDEA, RSA • Design the interceptors out-of-band interfaces • ACTs for encoding/decoding function and argument • Initial encoding function and argument • Design interceptor dispatcher and registry • Singleton for all interceptors in a process • Event handler for reactor, also an acceptor • How to federate across distribution boundaries?

  12. “Dynamic” Interceptor Variant • Base Interceptor • virtual apply_XXX() • Concrete Interceptors • Logging • Checksum • Statistics • Encrypt • security checks class Interceptor { public: virtual void apply_encode(string&) = 0; virtual void apply_decode(string&) = 0; };

  13. The Data_Endpoint class • Abstraction for sending and receiving data • Two Data_Endpoints connect then send • send_data() and receive_data() both dispatch to interceptors • add_interceptor() registers new concrete interceptors

  14. “Dynamic” Interceptor (1/3) // A simple Interceptor Function class that rot13's a message.class Rot13_Interceptor : public Interceptor { class Rot13 : public unary_function<char, char> { public: char operator()(char c) { if(islower(c)) return c + ((c >= 'a' + 13) ? -13 : 13); if(isupper(c)) return c + ((c >= 'A' + 13) ? -13 : 13); return c; } };public: void apply_encode(string& s) { transform(s.begin(), s.end(), s.begin(), Rot13()); } void apply_decode(string& s) { transform(s.begin(), s.end(), s.begin(), Rot13()); }};

  15. “Dynamic” Interceptor (2/3) class Data_Endpoint { Data_Endpoint *peer_; string *data_; typedef vector<Interceptor*> Interceptor_List; Interceptor_List interceptors_; // … public: // … void send_data(const string& s) { // not mt-safe! Encode_Dispatcher dispatch(s); for_each(interceptors_.begin(), interceptors_.end(), dispatch); string t = dispatch.get_result(); //send data to peer_ cout << "Data_Endpoint@" << this << " sent '" << t << "' on the wire" << endl; } // …}

  16. “Dynamic” Interceptor (3/3) class Encode_Dispatcher : public unary_function<Interceptor*, void> { string s_;public: Encode_Dispatcher(string s) : s_(s) { } void operator()(Interceptor* i) { i->apply_encode(s_); } string get_result(void) { return s_; }};

  17. Using “Dynamic” Interceptor Data_Endpoint foo; Data_Endpoint bar; Print_Interceptor *pf = new Print_Interceptor; Rot13_Interceptor *rot13 = new Rot13_Interceptor; foo.add_interceptor(pf); foo.add_interceptor(rot13); foo.add_interceptor(pf); bar.add_interceptor(pf); bar.add_interceptor(rot13); bar.add_interceptor(pf); foo.connect(bar); foo.send_data(“a string”);

  18. Analysis of Dynamic Interceptor • apply_XXX() design is fragile • To add a new interception type: • add pure virtual apply_foo() to base • implement in all existing concrete interceptors • or refactor to have a single apply() with a switch • May not need all of this flexibility • may not need per-event context objects • may not need to reorder or add interceptors at runtime

  19. Consequences + Separation of Concerns + (Future) Flexibility +Reusability/Portability −Efficiency/Heterogenity −Evil Interceptors

  20. Interceptors using Generics • Interceptor instances may not be needed (stateless) • No context objects • Note: one interceptor • Can’t do both checksum and encrypt directly here • Could compose both functions into interceptor • Or, could publish trigger event which other interceptors can handle Can specialize! template <class Interceptor> class Data_Endpoint { public: send() { ... Interceptor()(Send_Event()); ... } ... }; class Logging_Interceptor { public: void operator()(...) { ... } };

  21. Potential Problems • Data_Endpoint incompatibility • arises from instantiating the Data_Endpoint template with different types • connect() and send() no longer work • Solution: template <class Other_Interceptor> friend class Data_Endpoint; • Single Interceptor only • Can use Typelists to extend this approach!

  22. Typelists • List of types using templates • No inherent runtime overhead • Allows arbitrary number of Interceptors: template <class T, class U>struct Typelist { typedef T Head; typedef U Tail; }; Typelist<Logging_Interceptor, Typelist<Checksum_Interceptor, NullType> > Andrei Alexandrescu, Modern C++ Design: Generic Programming and Design Patterns Applied

  23. More on Typelists • Telescoping Typelist definitions awkward… • typelist.h defines: • Mechanism includes various algorithms: • Length, TypeAt (index), Append, Erase (type from Typelist), Replace (type with type), DerivedToFront • all “for free” – evaluated at compile time! #define TYPELIST_1(T1) Typelist<T1,NullType> #define TYPELIST_2(T1,T2) Typelist<T1,TYPELIST_1(T2) > #define TYPELIST_3(T1,T2,T3) Typelist<T1,TYPELIST_2(T2,T3) > ... up to TYPELIST_50(...)

  24. Using Generic Interceptors Data_Endpoint<Chain_Interceptor< TYPELIST_3(Print_Interceptor, Rot13_Interceptor, Print_Interceptor)> > foo; Data_Endpoint<Chain_Interceptor< TYPELIST_1(Rot13_Interceptor)> > bar; foo.connect(bar); foo.send_data(“Hello world”);

  25. Analysis of Generic Interceptors • No runtime modification of Interceptors • BUT:efficient (inlined) dispatch • Performance vs. Flexibility • Because of template use, it’s “compile-time configurable and flexible” • easy to change Interceptor attachment • but must recompile

More Related