File: chapter-5.html

package info (click to toggle)
libneedle-ruby 1.2.0-2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 1,436 kB
  • ctags: 886
  • sloc: ruby: 4,464; makefile: 52
file content (393 lines) | stat: -rw-r--r-- 14,842 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
<html>
  <head>
    <title>Needle Manual :: Chapter 5: Interceptors</title>
    <link type="text/css" rel="stylesheet" href="manual.css" />
  </head>
  
  <body>
    <div id="banner">
      <table border='0' cellpadding='0' cellspacing='0' width='100%'>
        <tr><td valign='top' align='left'>
          <div class="title">
            <span class="product">Needle&mdash;</span><br />
            <span class="tagline">to the point --></span>
          </div>
        </td><td valign='middle' align='right'>
          <div class="info">
            Needle Version: <strong>1.2.0</strong><br />
            Manual Last Updated: <strong>2004-11-18 15:36 GMT</strong>
          </div>
        </td></tr>
      </table>
    </div>

    <table border='0' width='100%' cellpadding='0' cellspacing='0'>
      <tr><td valign='top'>

        <div id="navigation">
          <h1>Needle Manual</h1>

          <h2>Chapters</h2>
          <ol type="I">
          
            <li>
                <a href="chapter-1.html">
                Introduction
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-1.html#s1">What is Needle?</a></li>
                
                  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
                
                  <li><a href="chapter-1.html#s3">Alternatives</a></li>
                
                  <li><a href="chapter-1.html#s4">License Information</a></li>
                
                  <li><a href="chapter-1.html#s5">Support</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-2.html">
                Registry
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-2.html#s1">Overview</a></li>
                
                  <li><a href="chapter-2.html#s2">Creating</a></li>
                
                  <li><a href="chapter-2.html#s3">Services</a></li>
                
                  <li><a href="chapter-2.html#s4">Namespaces</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-3.html">
                Service Locator
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-3.html#s1">Overview</a></li>
                
                  <li><a href="chapter-3.html#s2">Conventional Architecture</a></li>
                
                  <li><a href="chapter-3.html#s3">Locator Pattern</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-4.html">
                Dependency Injection
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-4.html#s1">Overview</a></li>
                
                  <li><a href="chapter-4.html#s2">Setup</a></li>
                
              </ol>
            </li>
          
            <li><strong>
                <a href="chapter-5.html">
                Interceptors
                </a>
                </strong> <big>&larr;</big>
              <ol type="1">
                
                  <li><a href="chapter-5.html#s1">Overview</a></li>
                
                  <li><a href="chapter-5.html#s2">Architecture</a></li>
                
                  <li><a href="chapter-5.html#s3">Attaching</a></li>
                
                  <li><a href="chapter-5.html#s4">Ordering</a></li>
                
                  <li><a href="chapter-5.html#s5">Custom</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-6.html">
                Service Models
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-6.html#s1">Overview</a></li>
                
                  <li><a href="chapter-6.html#s2">Pipelines</a></li>
                
                  <li><a href="chapter-6.html#s3">Models</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-7.html">
                Logging
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-7.html#s1">Overview</a></li>
                
                  <li><a href="chapter-7.html#s2">LogFactory</a></li>
                
                  <li><a href="chapter-7.html#s3">Configuration</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-8.html">
                Service Libraries
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-8.html#s1">Overview</a></li>
                
                  <li><a href="chapter-8.html#s2">Creating Libraries</a></li>
                
                  <li><a href="chapter-8.html#s3">Using Libraries</a></li>
                
              </ol>
            </li>
          
            <li>
                <a href="chapter-9.html">
                Customizing Needle
                </a>
                
              <ol type="1">
                
                  <li><a href="chapter-9.html#s1">Namespaces</a></li>
                
                  <li><a href="chapter-9.html#s2">Interceptors</a></li>
                
                  <li><a href="chapter-9.html#s3">Contexts</a></li>
                
              </ol>
            </li>
          
          </ol>

          <h2>Other Documentation</h2>

          <ul>
            <li><a href="http://needle.rubyforge.org/api/index.html">Needle API</a></li>
            <li><a href="http://needle.rubyforge.org/faq.html">Needle FAQ</a></li>
          </ul>

          <h2>Tutorials</h2>
          <ol>
          
          </ol>

          <p align="center"><strong>More To Come...</strong></p>

          <div class="license">
            <a href="http://creativecommons.org/licenses/by-sa/2.0/"><img alt="Creative Commons License" border="0" src="http://creativecommons.org/images/public/somerights" /></a><br />
            This manual is licensed under a <a href="http://creativecommons.org/licenses/by-sa/2.0/">Creative Commons License</a>.
          </div>
        </div>

      </td><td valign='top' width="100%">

        <div id="content">

          <h1>5. Interceptors</h1>



     <h2>
       <a name="s1"></a>
       5.1. Overview
     </h2>

   

   <div class="section">
     <p>Interceptors are objects (or blocks) that sit between a client and a service and <em>intercept</em> messages (methods) sent to the service. Each service may have many such interceptors. When control is passed to an interceptor, it may then do something before and after passing control to the next interceptor, possibly even returning instead of passing control. This allows for some simple <acronym title="Aspect-Oriented Programming">AOP</acronym>-like hooks to be placed on your services.</p>

	<p>Needle comes with one interceptor, the LoggingInterceptor. This allows you to easily trace the execution of your services by logging method entry and exit, as well as any exceptions that are raised.</p>

	<p>You can, of course, implement your own interceptors as well.</p>
   </div>



     <h2>
       <a name="s2"></a>
       5.2. Architecture
     </h2>

   

   <div class="section">
     <p>Interceptors are implemented as proxy objects that front the requested service. Thus, if you request a service that has 3 interceptors wrapped around it, you&#8217;re really getting a proxy object back that will invoke the interceptors (in order) before the requested method of the service is invoked.</p>

	<p>The interceptors themselves are attached to the service during the execution of its instantiation pipeline (see Service Models). Thus, any action in the pipeline that is situated closer to the service than the interceptor pipeline action will bypass the interceptors altogether. This allows you to attach hooks to your service that can be called without invoking the interceptors on the service.</p>

	<p>Another thing to keep in mind is that the interceptors are one-to-one for each service instance. Thus, if your service is a prototype (see the Service Models chapter), you&#8217;ll have one instance of each interceptor for each instance of your service.</p>
   </div>



     <h2>
       <a name="s3"></a>
       5.3. Attaching
     </h2>

   

   <div class="section">
     <p>There are two ways to attach interceptors to your services. The first is to implement an interceptor <em>factory</em> that returns new interceptor <em>instances</em>, and attach the factory to your service. The second is to specify a block that implements the required functionality. Both have their uses.</p>

	<h3>Interceptor Factories</h3>

	<p>Interceptor factories are useful in situations where you want to implement some functionality and have it apply to multiple services. Interceptors from factories are also faster (less overhead) than interceptors from blocks, and so might be appropriate where performance is an issue.</p>

	<p>An example is the LoggingInterceptor that ships with Needle. Because it is functionality that could be used on any number of services, it is implemented as a factory.</p>

	<p>You can attach interceptor factories to your service using the <code>#interceptor(...).with {...}</code> syntax:</p>


<pre>
  reg.register( :foo ) {...}
  reg.intercept( :foo ).with { MyInterceptorFactory }
</pre>
	<p>Note that you could also make the interceptor factory a service:</p>


<pre>
  reg.register( :foo ) {...}
  reg.register( :my_interceptor ) { MyInterceptorFactory }
  reg.intercept( :foo ).with { |c| c.my_interceptor }
</pre>
	<p>And, to make accessing interceptor services even more convenient, you can use the <code>#with!</code> method (which executes its block within the context of the calling container):</p>


<pre>
  reg.register( :foo ) {...}
  reg.register( :my_interceptor ) { MyInterceptorFactory }
  reg.intercept( :foo ).with! { my_interceptor }
</pre>
	<h3>Blocks</h3>

	<p>Sometimes creating an entire class to implement an interceptor is overkill. This is particularly the case during debugging or testing, when you might want to attach an interceptor to class to verify that a parameter passed is correct, or a return value is what you expect. To satisfy these conditions, you can using the
<code>#doing</code> method.  Just give it a block that accepts two parameters (the chain, and context) and you&#8217;re good to go!</p>


<pre>
  reg.register( :foo ) {...}
  reg.intercept( :foo ).doing { |chain,ctx| ...; chain.process_next( ctx ) }
</pre>
	<p>Note that this approach is about 40% slower than using an interceptor factory, so it should not be used if performance is an issue.</p>

	<h3>Options</h3>

	<p>Some interceptors can accept configuration options. For example, the LoggingInterceptor allows clients to specify methods that should and shouldn&#8217;t be intercepted. Options are specified via the <code>#with_options</code> method.</p>


<pre>
  reg.register( :foo ) {...}
  reg.intercept( :foo ).
    with { |c| c.logging_interceptor }.
    with_options( :exclude =&gt; [ "method1", "method2" ] )
</pre>
	<p>Options can apply to the blocks given to the <code>#doing</code> method, too. The block may access the options via the <code>#data[:options]</code> member of the context:</p>


<pre>
  reg.intercept( :foo ).
    doing { |ch,ctx| ...; p ctx.data[:options][:value]; ... }.
    with_options( :value =&gt; "hello" )
</pre>
	<p>With blocks, of course, the value of such an approach is limited.</p>
   </div>



     <h2>
       <a name="s4"></a>
       5.4. Ordering
     </h2>

   

   <div class="section">
     <p>As was mentioned, a service may have multiple interceptors attached to it. By default, a method will be filtered by the interceptors in the same order that they were attached, with the first interceptor that was attached being the first one to intercept every method call.</p>

	<p>You can specify a different ordering of the interceptors by giving each one a <em>priority</em>. The priority is a number, where interceptors with a higher priority sort <em>closer</em> to the service, and those with lower priorities sort <em>further</em> from the service.</p>

	<p>You can specify the priority as an option when attaching an interceptor:</p>


<pre>
  reg.register( :foo ) { ... }
  reg.intercept( :foo ).with { Something }.with_options( :priority =&gt; 100 )
  reg.intercept( :foo ).with { SomethingElse }.with_options( :priority =&gt; 50 )
</pre>
	<p>Without the priorities, when a method of <code>:foo</code> was invoked, Something would be called first, and then SomethingElse. <em>With</em> the priorities (as specified), SomethingElse would be called before Something (since SomethingElse has a lower priority).</p>
   </div>



     <h2>
       <a name="s5"></a>
       5.5. Custom
     </h2>

   

   <div class="section">
     <p>Creating your own interceptors is very easy. As was demonstrated earlier, you can always use blocks to implement an interceptor. However, for more complex interceptors, or for interceptors that you want to reuse across multiple services, you can also implement your own interceptor <em>factories</em>.</p>

	<p>An interceptor factory can be any object, as long as it implements the method <code>#new</code> with two parameters, the <em>service point</em> (service &#8220;definition&#8221;) of the service that the interceptor will be bound to, and a hash of the options that were passed to the interceptor when it was attached to the service. This method should then return a new interceptor instance, which must implement the <code>#process</code> method. The <code>#process</code> method should accept two parameters: an object representing the <em>chain</em> of interceptors, and the invocation <em>context</em>.</p>


<pre>
  class MyInterceptorFactory
    def initialize( point, options )
      ...
    end

    def process( chain, context )
      # context.sym   : the name of the method that was invoked
      # context.args  : the array of arguments passed to the method
      # context.block : the block passed to the method, if any
      # context.data  : a hash that may be used to share data between interceptors
      return context.process_next( context )
    end
  end
</pre>
	<p>Once you&#8217;ve created your factory, you can attach it to a service:</p>


<pre>
  reg.intercept( :foo ).with { MyInterceptorFactory }
</pre>
   </div>




        </div>

      </td></tr>
    </table>
  </body>
</html>