...
ServiceCallContext - immutable user parameter map that will 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); } |
New methods in IgniteServices to pass caller context to service proxy.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
public <T> T serviceProxy(String name, Class<? super T> svcItf, boolean sticky, ServiceCallContext callCtx)
public <T> T serviceProxy(String name, Class<? super T> svcItf, boolean sticky, ServiceCallContext callCtx, long timeout) |
New method in ServiceContext for getting caller context inside the service method.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/**
* Gets context of the current service call.
*
* @return Context of the current service call, possibly {@code null}.
* @see ServiceCallContext
*/
@Nullable public ServiceCallContext currentCallContext(); |
New property in ServiceResource annotation.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/**
* Flag indicating that the service call context should be passed to the injected service.
*
* @return {@code True} if the service call context should be passed to the injected service.
*/
public boolean forwardCallerContext() default false; |
The user can create context (map with custom parameters) and bind it to the service proxy. After that, each call to the proxy method will also implicitly pass context parameters to the service.
Service method can read current context parameters using ServiceContext#currentCallContext method. It is only accessible from the current thread during the execution of a service method.
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.
ServiceCallContext should be available only on server side.
The user can bind a custom map/dictionary of parameters to the service proxy and read them from the call context on the server side.
So, some current public APIs need to be changed (all discussed API are experimental):
Rework methods in IgniteServices to allow user to bind Map of attributes (instead of ServiceCallContext) to service proxy.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
public <T> T serviceProxy(String name, Class<? super T> svcItf, boolean sticky, Map<String, Object> callAttrs)
public <T> T serviceProxy(String name, Class<? super T> svcItf, boolean sticky, Map<String, Object> callAttrs, long timeout) |
ServiceCallInterceptor - intercepts service method calls.
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
public interface ServiceCallInterceptor extends Serializable { // Called BEFORE the service method is executed. public default void onInvoke(ServiceCallContextServiceInterceptorContext ctx) throws ServiceInterceptException { // No-op. } // Called AFTER the service method is executed. public default void onComplete(@Nullable Object res, ServiceCallContextServiceInterceptorContext ctx) throws ServiceInterceptException { // No-op. } // Called when onInvoke, onComplete or service method throws an exception. public default void onError(Throwable err, ServiceCallContextServiceInterceptorContext ctx) { // No-op. } } |
ServiceCallContext ServiceInterceptorContext - extended mutable version of caller context (interceptor obtains method call parameters from it and can use it to update the user attributescaller context).
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
public interface ServiceInterceptorContext extends ServiceCallContext { public String method(); public @Nullable Object[] arguments(); public Map<String, Object> callAttributes(void attribute(String name, String val); public void binaryAttribute(String name, byte[] val); } |
language | java |
---|---|
theme | RDark |
title | ServiceCallContext.java |
collapse | true |
draw.io Diagram border true diagramName middleware simpleViewer false widthlinks auto tbstyle top lbox true diagramWidth 1001 revision 13
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
ServiceCallInterceptor security = new ServiceCallInterceptor() { @Override public void onInvoke(ServiceInterceptorContext ctx) throws ServiceInterceptException { // Check permission before execution of the method. if (!CustomSecurityProvider.get().access(ctx.method(), ctx.attribute("sessionId"))) throw new SecurityException("Method invocation is not permitted"); } } ServiceCallInterceptor audit = new ServiceCallInterceptor() { @Override public void onInvoke(ServiceInterceptorContext ctx) { // Record an event before execution of the method. AuditProvider.get().recordStartEvent(ctx.method(), ctx.attribute("sessionId")); } @Override public void onComplete(@Nullable Object res, ServiceInterceptorContext ctx) { AuditProvider.get().recordFinishEvent(ctx.method(), ctx.attribute("sessionId")); } @Override public void onError(Throwable err, ServiceInterceptorContext ctx) { AuditProvider.get().recordError(ctx.method(), ctx.attribute("sessionId"), err.getMessage()); } } // Set callcontext attributesparameters for service proxy. Map<String,ServiceCallContext String>ctx attrs = new HashMap<>(); attrsServiceCallContext.builder().put("sessionId", sessionId).build(); ServiceConfigurationServiceConfiguration svcCfg = new ServiceConfiguration() .setName("service") .setService(new MyServiceImpl()) .setMaxPerNodeCount(1) .setInterceptors(security, audit); // Deploy service. ignite.services().deploy(svcCfg); MyService proxy = ignite.services().serviceProxy("service", MyService.class, false, attrsctx, 0); // A business method call to be intercepted. proxy.placeOrder(order1); proxy.placeOrder(order2); |
One service can have several interceptors. They are defined using the service configuration and deployed with the service.
...
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).
Interceptor must support ignite instance resource injection.
Interceptor should support LifeCycleAware
Interceptor only applies to user-defined service methods and does not apply to service lifecycle methods - init, execute andcancel,
The user can create context (map with custom parameters map ) and bind it to the service proxy. After that, each call to the proxy method will also implicitly pass context parameters to the service.
Service method can read current context parameters using ServiceContext#currentCallContext#callAttributes ServiceContext#currentCallContext method. It is only accessible from the current thread during the execution of a service method.
Interceptor can read and update context parameters using ServiceCallContext ServiceInterceptorContext.
If an interceptor has been specified, but the user has not passed the caller context through the proxy, then for each call to the service method, an empty context will be dynamically created.
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.
...