aboutsummaryrefslogtreecommitdiffstats
path: root/messagebus/src/main/java/com/yahoo/messagebus/routing/RoutingContext.java
blob: 18b5de34bb4e49b3a72e2b5f1c1a846bccd246dd (plain) (blame)
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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.messagebus.routing;

import com.yahoo.jrt.slobrok.api.IMirror;
import com.yahoo.messagebus.Error;
import com.yahoo.messagebus.*;

import java.util.*;

/**
 * <p>This context object is what is seen by {@link RoutingPolicy} when doing
 * both select() and merge(). It contains the necessary accessors to everything
 * a policy is expected to need. An instance of this is created for every {@link
 * RoutingNode} that contains a policy.</p>
 *
 * @author Simon Thoresen Hult
 */
public class RoutingContext {

    private final RoutingNode node;
    private final int directive;
    private final Set<Integer> consumableErrors = new HashSet<>();
    private boolean selectOnRetry = true;
    private Object context = null;

    /**
     * <p>Constructs a new routing context for a given routing node and hop.</p>
     *
     * @param node      The owning routing node.
     * @param directive The index to the policy directive of the hop.
     */
    RoutingContext(RoutingNode node, int directive) {
        this.node = node;
        this.directive = directive;
    }

    public String toString() {
        return "node : " + node + ", directive: " + directive + ", errors: " + consumableErrors +
                ", selectOnRetry: " + selectOnRetry + " context: " + context;
    }

    /**
     * <p>Returns whether or not this hop has any configured recipients.</p>
     *
     * @return True if there is at least one recipient.
     */
    public boolean hasRecipients() {
        return !node.getRecipients().isEmpty();
    }

    /**
     * <p>Returns the number of configured recipients for this hop.</p>
     *
     * @return The recipient count.
     */
    public int getNumRecipients() {
        return node.getRecipients().size();
    }

    /**
     * <p>Returns the configured recipient at the given index.</p>
     *
     * @param idx The index of the recipient to return.
     * @return The reipient at the given index.
     */
    public Route getRecipient(int idx) {
        return node.getRecipients().get(idx);
    }

    /**
     * <p>Returns all configured recipients for this hop.</p>
     *
     * @return An unmodifiable list of recipients.
     */
    public List<Route> getAllRecipients() {
        return Collections.unmodifiableList(node.getRecipients());
    }

    /**
     * <p>Returns a list of all configured recipients whose first hop matches
     * this.</p>
     *
     * @return A modifiable list of recipients.
     */
    public List<Route> getMatchedRecipients() {
        List<Route> ret = new ArrayList<>();
        Set<String> done = new HashSet<>();
        Hop hop = getHop();
        for (Route route : node.getRecipients()) {
            if (route.hasHops() && hop.matches(route.getHop(0))) {
                HopDirective dir = route.getHop(0).getDirective(directive);
                String key = dir.toString();
                if (!done.contains(key)) {
                    ret.add(new Route(route).setHop(0, new Hop(hop).setDirective(directive, dir)));
                    done.add(key);
                }
            }
        }
        return ret;
    }

    /**
     * <p>Returns whether or not the policy is required to reselect if resending
     * occurs.</p>
     *
     * @return True to invoke {@link RoutingPolicy#select(RoutingContext)} on
     *         resend.
     */
    public boolean getSelectOnRetry() {
        return selectOnRetry;
    }

    /**
     * <p>Sets whether or not the policy is required to reselect if resending
     * occurs.</p>
     *
     * @param selectOnRetry The value to set.
     * @return This, to allow chaining.
     */
    public RoutingContext setSelectOnRetry(boolean selectOnRetry) {
        this.selectOnRetry = selectOnRetry;
        return this;
    }

    /**
     * <p>Returns the route that contains the routing policy that spawned
     * this.</p>
     *
     * @return The route.
     */
    public Route getRoute() {
        return node.getRoute();
    }

    /**
     * <p>Returns the hop that contains the routing policy that spawned
     * this.</p>
     *
     * @return The hop.
     */
    public Hop getHop() {
        return node.getRoute().getHop(0);
    }

    /**
     * <p>Returns the index of the hop directive that spawned this.</p>
     *
     * @return The directive index.
     */
    public int getDirectiveIndex() {
        return directive;
    }

    /**
     * <p>Returns the policy directive that spawned this.</p>
     *
     * @return The directive object.
     */
    public PolicyDirective getDirective() {
        return (PolicyDirective)getHop().getDirective(directive);
    }

    /**
     * <p>Returns the part of the route string that precedes the active policy
     * directive. This is the same as calling {@link #getHop()}.getPrefix({@link
     * #getDirectiveIndex()}).</p>
     *
     * @return The hop prefix.
     */
    public String getHopPrefix() {
        return getHop().getPrefix(directive);
    }

    /**
     * <p>Returns the remainder of the route string immediately following the
     * active policy directive. This is the same as calling {@link
     * #getHop()}.getSuffix({@link #getDirectiveIndex()}).</p>
     *
     * @return The hop suffix.
     */
    public String getHopSuffix() {
        return getHop().getSuffix(directive);
    }

    /**
     * <p>Returns the policy specific context object.</p>
     *
     * @return The context.
     */
    public Object getContext() {
        return context;
    }

    /**
     * <p>Sets a policy specific context object that will be available at
     * merge().</p>
     *
     * @param context An arbitrary object.
     * @return This, to allow chaining.
     */
    public RoutingContext setContext(Object context) {
        this.context = context;
        return this;
    }

    /**
     * <p>Returns the message being routed.</p>
     *
     * @return The message.
     */
    public Message getMessage() {
        return node.getMessage();
    }

    /**
     * <p>Adds a string to the trace of the message being routed.</p>
     *
     * @param level The level of the trace note.
     * @param note  The note to add.
     */
    public void trace(int level, String note) {
        node.getTrace().trace(level, note);
    }

    /**
     * Indicates if tracing is enabled at this level.
     * @param level the level
     * @return  true if tracing is enabled at this level
     */
    public boolean shouldTrace(int level) {
        return node.getTrace().shouldTrace(level);
    }

    /**
     * <p>Returns whether or not a reply is available.</p>
     *
     * @return True if a reply is set.
     */
    public boolean hasReply() {
        return node.hasReply();
    }

    /**
     * <p>Returns the reply generated by the associated routing policy.</p>
     *
     * @return The reply.
     */
    public Reply getReply() {
        return node.getReply();
    }

    /**
     * <p>Sets the reply generated by the associated routing policy.</p>
     *
     * @param reply The reply to set.
     * @return This, to allow chaining.
     */
    public RoutingContext setReply(Reply reply) {
        node.setReply(reply);
        return this;
    }

    /**
     * <p>This is a convenience method to call {@link #setError(Error)}.</p>
     *
     * @param code The code of the error to set.
     * @param msg  The message of the error to set.
     * @return This, to allow chaining.
     */
    public RoutingContext setError(int code, String msg) {
        node.setError(code, msg);
        return this;
    }

    /**
     * <p>This is a convenience method to assign an {@link EmptyReply}
     * containing a single error to this. This also fiddles with the trace
     * object so that the error gets written to it.</p>
     *
     * @param err The error to set.
     * @return This, to allow chaining.
     * @see #setReply(Reply)
     */
    public RoutingContext setError(Error err) {
        node.setError(err);
        return this;
    }

    /**
     * <p>Returns the message bus instance on which this is running.</p>
     *
     * @return The message bus.
     */
    public MessageBus getMessageBus() {
        return node.getMessageBus();
    }

    /**
     * <p>Returns whether or not the owning routing node has any child
     * nodes.</p>
     *
     * @return True if there is at least one child.
     */
    public boolean hasChildren() {
        return !node.getChildren().isEmpty();
    }

    /**
     * <p>Returns the number of children the owning routing node has.</p>
     *
     * @return The child count.
     */
    public int getNumChildren() {
        return node.getChildren().size();
    }

    /**
     * <p>Returns an iterator for the child routing nodes of the owning
     * node.</p>
     *
     * @return The iterator.
     */
    public RoutingNodeIterator getChildIterator() {
        return new RoutingNodeIterator(node.getChildren());
    }

    /**
     * <p>Adds a child routing context to this based on a given route. This is
     * the typical entry point a policy will use to select recipients during a
     * {@link RoutingPolicy#select(RoutingContext)} invocation.</p>
     *
     * @param route The route to contain in the child context.
     */
    public void addChild(Route route) {
        node.addChild(route);
    }

    /**
     * <p>This is a convenience method to more easily add a list of children to
     * this. It will simply call the {@link #addChild} method for each element
     * in the list.</p>
     *
     * @param routes A list of routes to add as children.
     */
    public void addChildren(List<Route> routes) {
        if (routes != null) {
            for (Route route : routes) {
                addChild(route);
            }
        }
    }

    /**
     * <p>Returns the local mirror of the system's name server.</p>
     *
     * @return The mirror api.
     */
    public IMirror getMirror() {
        return node.getNetwork().getMirror();
    }

    /**
     * <p>Adds the given error code to the list of codes that the associated
     * routing policy <u>may</u> consume. This is used to verify whether or not
     * a resolved routing tree can succeed if sent. Because verification is only
     * done before sending, the error types that must be added here are only
     * those that can be generated by message bus itself.</p>
     *
     * @param errorCode The code that might be consumed.
     * @see RoutingNode#getUnconsumedErrors()
     * @see com.yahoo.messagebus.ErrorCode
     */
    public void addConsumableError(int errorCode) {
        consumableErrors.add(errorCode);
    }

    /**
     * <p>Returns whether or not the given error code <u>may</u> be consumed by
     * the associated routing policy.</p>
     *
     * @param errorCode The code to check.
     * @return True if the code may be consumed.
     * @see #addConsumableError(int)
     */
    public boolean isConsumableError(int errorCode) {
        return consumableErrors.contains(errorCode);
    }
}