...
ServiceCallContext - immutable user parameter map that will can be implicitly passed to the service (and interceptor) on every method call.
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
public interface ServiceCallContext { public String attribute(String name); public byte[] binaryAttribute(String name); } |
ServiceCallInterceptor - intercepts service method calls.
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
public interface ServiceCallInterceptor extends Serializable { //** Called BEFORE the service method* isIntercepts executed. a call to a publicservice defaultmethod. void onInvoke(String mtd, Object[] args, @Nullable ServiceCallContext callCtx) throws ServiceInterceptException { * * @param mtd // No-opMethod name. } * //@param Calledargs AFTER the service method is executedMethod arguments. public default* void@param onComplete(String mtd, @Nullable Object res, @Nullable ServiceCallContext callCtx) throws ServiceInterceptException {ctx Service context. * @param call // No-opDelegated call. } * //@return CalledService whenmethod onInvoke, onComplete or service method throws an exception.call result. */ public defaultObject void onErrorinvoke(String mtd, ThrowableObject[] errargs, @Nullable ServiceCallContext callCtx) { // No-op. } } |
ServiceContext ctx, Callable<Object> call) throws Exception;
} |
draw.io Diagram border true diagramName middleware simpleViewer false links auto tbstyle top lbox true diagramWidth 1001 revision 13
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
ServiceCallInterceptor security = new ServiceCallInterceptor() { @Override public void onInvoke(String mtd, Object[] args, ServiceCallContextctx, ctxsvcCall) -> { // Check permission before execution of the method. if (!CustomSecurityProvider.get().access(mtd, ctx.currentCallContext().attribute("sessionId"))) throw new SecurityException("Method invocation is not permitted"); // Execute remaining interceptors and service }method. } return svcCall.call(); }; ServiceCallInterceptor audit = new ServiceCallInterceptor() { mtd, args, ctx, svcCall) -> { @OverrideString publicsessionId void onInvoke(String mtd, Object[] args, ServiceCallContext ctx) { = ctx.currentCallContext().attribute("sessionId"); AuditProvider prov = AuditProvider.get(); // Record an event before execution of the method. AuditProvider.get().recordStartEvent(mtd, ctx.attribute("sessionId"));prov.recordStartEvent(ctx.name(), mtd, sessionId); try { } // Execute service method. @Override public void onComplete(String mtd, @Nullable Object res, ServiceCallContext ctx) { svcCall.call(); } catch AuditProvider.get().recordFinishEvent(mtd, ctx.attribute("sessionId"));Exception e) { } // Record error. @Override public void onError(String prov.recordError(ctx.name(), mtd, Throwable errsessionId), ServiceCallContext ctx) {e.getMessage()); // Re-throw exception to AuditProvider.get().recordError(mtd, ctx.attribute("sessionId"), err.getMessage()); initiator. throw e; } } finally { // Set context parameters for Record finish event after execution of the service proxymethod. ServiceCallContext ctx = ServiceCallContextprov.builderrecordFinishEvent()ctx.put("sessionId"name(), mtd, sessionId).build(); } } ServiceConfiguration svcCfg = new ServiceConfiguration() .setName("service") .setService(new MyServiceImpl()) .setMaxPerNodeCount(1) .setInterceptors(security, audit); // Deploy service. ignite.services().deploy(svcCfg); // DeploySet context parameters for the service proxy. ServiceCallContext callCtx ignite.services().deploy(svcCfg= ServiceCallContext.builder().put("sessionId", sessionId).build(); // Make service proxy. MyService proxy = ignite.services().serviceProxy("service", MyService.class, false, ctxcallCtx, 0); // A business method call to be intercepted. proxy.placeOrder(order1); proxy.placeOrder(order2); |
...
To add/remove interceptor service should be redeployed.
Interceptor is located and executed where the service is implemented (for Java service - on Java side, for .NET-service on .NET side). Its execution should not cause additional serialization).
...
The user can specify multiple interceptors.Each interceptor invokes the next interceptor in the chain using a delegated call, the last interceptor will call the service method.
So the interceptor specified first in the configuration will process the result of the service method execution last.
draw.io Diagram | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Interceptor must support ignite instance resource injection. Interceptor should support LifeCycleAwarethe injection of generic resources.
Interceptor only applies to user-defined service methods and does not apply to service lifecycle methods - init, execute andcancel,
...
If one service calls another, then by default the current call context will not be bound to the created proxy - the user must explicitly bind it. But Java service has a special ServiceResource annotation to inject another service proxy into the current service. If the user wants to redirect the current call context to this (injected) proxy, he can set the forwardCallerContext option of this annotation.
Interceptor can only throw unchecked exceptions.
Any runtime exception Exception thrown by the onInvoke/onComplete methods interceptor will be wrapped in a ServiceInterceptException.This exception will be into unchecked IgniteException and passed to the initiator (user) and to onError method of the interceptor.
If onInvoke throws an exception, then the service method will not be called.
The exception thrown by the onError method will be added to the main exception as suppressed.
...
.
The interceptor gives the user full control over the invocation of the service methods, so in case of implementation errors, the user may get unexpected behavior of the service.
...