/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.ignite;

import java.util.Collection;
import org.apache.ignite.cluster.ClusterGroup;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.lang.IgniteAsyncSupport;
import org.apache.ignite.lang.IgniteAsyncSupported;
import org.apache.ignite.lang.IgniteExperimental;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.services.Service;
import org.apache.ignite.services.ServiceCallContext;
import org.apache.ignite.services.ServiceConfiguration;
import org.apache.ignite.services.ServiceDeploymentException;
import org.apache.ignite.services.ServiceDescriptor;
import org.jetbrains.annotations.Nullable;

/**
 * Defines functionality necessary to deploy distributed services on the grid.
 * <p>
 * Instance of {@code IgniteServices} which spans all cluster nodes can be obtained from Ignite as follows:
 * <pre class="brush:java">
 * Ignite ignite = Ignition.ignite();
 *
 * IgniteServices svcs = ignite.services();
 * </pre>
 * You can also obtain an instance of the services facade over a specific cluster group:
 * <pre class="brush:java">
 * // Cluster group over remote nodes (excluding the local node).
 * ClusterGroup remoteNodes = ignite.cluster().forRemotes();
 *
 * // Services instance spanning all remote cluster nodes.
 * IgniteServices svcs = ignite.services(remoteNodes);
 * </pre>
 * <p>
 * With distributed services you can do the following:
 * <ul>
 * <li>Automatically deploy any number of service instances on the grid.</li>
 * <li>
 *     Automatically deploy singletons, including <b>cluster-singleton</b>,
 *     <b>node-singleton</b>, or <b>key-affinity-singleton</b>.
 * </li>
 * <li>Automatically deploy services on node start-up by specifying them in grid configuration.</li>
 * <li>Undeploy any of the deployed services.</li>
 * <li>Get information about service deployment topology within the grid.</li>
 * </ul>
 * <h1 class="header">Deployment From Configuration</h1>
 * In addition to deploying managed services by calling any of the provided {@code deploy(...)} methods,
 * you can also automatically deploy services on startup by specifying them in {@link IgniteConfiguration}
 * like so:
 * <pre name="code" class="java">
 * IgniteConfiguration cfg = new IgniteConfiguration();
 *
 * ServiceConfiguration svcCfg1 = new ServiceConfiguration();
 *
 * // Cluster-wide singleton configuration.
 * svcCfg1.setName("myClusterSingletonService");
 * svcCfg1.setMaxPerNodeCount(1);
 * svcCfg1.setTotalCount(1);
 * svcCfg1.setService(new MyClusterSingletonService());
 *
 * ServiceConfiguration svcCfg2 = new ServiceConfiguration();
 *
 * // Per-node singleton configuration.
 * svcCfg2.setName("myNodeSingletonService");
 * svcCfg2.setMaxPerNodeCount(1);
 * svcCfg2.setService(new MyNodeSingletonService());
 *
 * cfg.setServiceConfiguration(svcCfg1, svcCfg2);
 * ...
 * Ignition.start(cfg);
 * </pre>
 * <h1 class="header">Load Balancing</h1>
 * In all cases, other than singleton service deployment, Ignite will automatically make sure that
 * an about equal number of services are deployed on each node within the grid. Whenever cluster topology
 * changes, Ignite will re-evaluate service deployments and may re-deploy an already deployed service
 * on another node for better load balancing.
 * <h1 class="header">Fault Tolerance</h1>
 * Ignite guarantees that services are deployed according to specified configuration regardless
 * of any topology changes, including node crashes.
 * <h1 class="header">Resource Injection</h1>
 * All distributed services can be injected with
 * ignite resources. Both, field and method based injections are supported. The following ignite
 * resources can be injected:
 * <ul>
 * <li>{@link IgniteInstanceResource}</li>
 * <li>{@link org.apache.ignite.resources.LoggerResource}</li>
 * <li>{@link org.apache.ignite.resources.SpringApplicationContextResource}</li>
 * <li>{@link org.apache.ignite.resources.SpringResource}</li>
 * </ul>
 * Refer to corresponding resource documentation for more information.
 * <h1 class="header">Service Example</h1>
 * Here is an example of how an distributed service may be implemented and deployed:
 * <pre name="code" class="java">
 * // Simple service implementation.
 * public class MyIgniteService implements Service {
 *      ...
 *      // Example of ignite resource injection. All resources are optional.
 *      // You should inject resources only as needed.
 *      &#64;IgniteInstanceResource
 *      private Ignite ignite;
 *      ...
 *      &#64;Override public void cancel(ServiceContext ctx) {
 *          // No-op.
 *      }
 *
 *      &#64;Override public void execute(ServiceContext ctx) {
 *          // Loop until service is cancelled.
 *          while (!ctx.isCancelled()) {
 *              // Do something.
 *              ...
 *          }
 *      }
 *  }
 * ...
 * IgniteServices svcs = ignite.services();
 *
 * svcs.deployClusterSingleton("mySingleton", new MyIgniteService());
 * </pre>
 */
public interface IgniteServices extends IgniteAsyncSupport {
    /**
     * Gets the cluster group to which this {@code IgniteServices} instance belongs.
     *
     * @return Cluster group to which this {@code IgniteServices} instance belongs.
     */
    public ClusterGroup clusterGroup();

    /**
     * Deploys a cluster-wide singleton service. Ignite will guarantee that there is always
     * one instance of the service in the cluster. In case if grid node on which the service
     * was deployed crashes or stops, Ignite will automatically redeploy it on another node.
     * However, if the node on which the service is deployed remains in topology, then the
     * service will always be deployed on that node only, regardless of topology changes.
     * <p>
     * Note that in case of topology changes, due to network delays, there may be a temporary situation
     * when a singleton service instance will be active on more than one node (e.g. crash detection delay).
     * <p>
     * This method is analogous to calling
     * {@link #deployMultiple(String, org.apache.ignite.services.Service, int, int) deployMultiple(name, svc, 1, 1)}
     * method.
     *
     * @param name Service name.
     * @param svc Service instance.
     * @throws ServiceDeploymentException If failed to deploy service.
     */
    @IgniteAsyncSupported
    public void deployClusterSingleton(String name, Service svc) throws ServiceDeploymentException;

    /**
     * Asynchronously deploys a cluster-wide singleton service. Ignite will guarantee that there is always
     * one instance of the service in the cluster. In case if grid node on which the service
     * was deployed crashes or stops, Ignite will automatically redeploy it on another node.
     * However, if the node on which the service is deployed remains in topology, then the
     * service will always be deployed on that node only, regardless of topology changes.
     * <p>
     * Note that in case of topology changes, due to network delays, there may be a temporary situation
     * when a singleton service instance will be active on more than one node (e.g. crash detection delay).
     * <p>
     * This method is analogous to calling
     * {@link #deployMultipleAsync(String, org.apache.ignite.services.Service, int, int)
     * deployMultipleAsync(name, svc, 1, 1)} method.
     *
     * @param name Service name.
     * @param svc Service instance.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> deployClusterSingletonAsync(String name, Service svc);

    /**
     * Deploys a per-node singleton service. Ignite will guarantee that there is always
     * one instance of the service running on each node. Whenever new nodes are started
     * within the underlying cluster group, Ignite will automatically deploy one instance of
     * the service on every new node.
     * <p>
     * This method is analogous to calling
     * {@link #deployMultiple(String, org.apache.ignite.services.Service, int, int) deployMultiple(name, svc, 0, 1)}
     * method.
     *
     * @param name Service name.
     * @param svc Service instance.
     * @throws ServiceDeploymentException If failed to deploy service.
     */
    @IgniteAsyncSupported
    public void deployNodeSingleton(String name, Service svc) throws ServiceDeploymentException;

    /**
     * Asynchronously deploys a per-node singleton service. Ignite will guarantee that there is always
     * one instance of the service running on each node. Whenever new nodes are started
     * within the underlying cluster group, Ignite will automatically deploy one instance of
     * the service on every new node.
     * <p>
     * This method is analogous to calling
     * {@link #deployMultipleAsync(String, org.apache.ignite.services.Service, int, int)
     * deployMultipleAsync(name, svc, 0, 1)} method.
     *
     * @param name Service name.
     * @param svc Service instance.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> deployNodeSingletonAsync(String name, Service svc);

    /**
     * Deploys one instance of this service on the primary node for a given affinity key.
     * Whenever topology changes and primary node assignment changes, Ignite will always
     * make sure that the service is undeployed on the previous primary node and deployed
     * on the new primary node.
     * <p>
     * Note that in case of topology changes, due to network delays, there may be a temporary situation
     * when a service instance will be active on more than one node (e.g. crash detection delay).
     * <p>
     * This method is analogous to the invocation of {@link #deploy(org.apache.ignite.services.ServiceConfiguration)}
     * method as follows:
     * <pre name="code" class="java">
     *     ServiceConfiguration cfg = new ServiceConfiguration();
     *
     *     cfg.setName(name);
     *     cfg.setService(svc);
     *     cfg.setCacheName(cacheName);
     *     cfg.setAffinityKey(affKey);
     *     cfg.setTotalCount(1);
     *     cfg.setMaxPerNodeCount(1);
     *
     *     ignite.services().deploy(cfg);
     * </pre>
     *
     * @param name Service name.
     * @param svc Service instance.
     * @param cacheName Name of the cache on which affinity for key should be calculated, {@code null} for
     *      default cache.
     * @param affKey Affinity cache key.
     * @throws ServiceDeploymentException If failed to deploy service.
     */
    @IgniteAsyncSupported
    public void deployKeyAffinitySingleton(String name, Service svc, @Nullable String cacheName, Object affKey)
        throws ServiceDeploymentException;

    /**
     * Asynchronously deploys one instance of this service on the primary node for a given affinity key.
     * Whenever topology changes and primary node assignment changes, Ignite will always
     * make sure that the service is undeployed on the previous primary node and deployed
     * on the new primary node.
     * <p>
     * Note that in case of topology changes, due to network delays, there may be a temporary situation
     * when a service instance will be active on more than one node (e.g. crash detection delay).
     * <p>
     * This method is analogous to the invocation of
     * {@link #deployAsync(org.apache.ignite.services.ServiceConfiguration)} method as follows:
     * <pre name="code" class="java">
     *     ServiceConfiguration cfg = new ServiceConfiguration();
     *
     *     cfg.setName(name);
     *     cfg.setService(svc);
     *     cfg.setCacheName(cacheName);
     *     cfg.setAffinityKey(affKey);
     *     cfg.setTotalCount(1);
     *     cfg.setMaxPerNodeCount(1);
     *
     *     ignite.services().deployAsync(cfg);
     * </pre>
     *
     * @param name Service name.
     * @param svc Service instance.
     * @param cacheName Name of the cache on which affinity for key should be calculated, {@code null} for
     *      default cache.
     * @param affKey Affinity cache key.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> deployKeyAffinitySingletonAsync(String name, Service svc, @Nullable String cacheName,
        Object affKey);

    /**
     * Deploys multiple instances of the service on the grid. Ignite will deploy a
     * maximum amount of services equal to {@code 'totalCnt'} parameter making sure that
     * there are no more than {@code 'maxPerNodeCnt'} service instances running
     * on each node. Whenever topology changes, Ignite will automatically rebalance
     * the deployed services within cluster to make sure that each node will end up with
     * about equal number of deployed instances whenever possible.
     * <p>
     * Note that at least one of {@code 'totalCnt'} or {@code 'maxPerNodeCnt'} parameters must have
     * value greater than {@code 0}.
     * <p>
     * This method is analogous to the invocation of {@link #deploy(org.apache.ignite.services.ServiceConfiguration)}
     * method as follows:
     * <pre name="code" class="java">
     *     ServiceConfiguration cfg = new ServiceConfiguration();
     *
     *     cfg.setName(name);
     *     cfg.setService(svc);
     *     cfg.setTotalCount(totalCnt);
     *     cfg.setMaxPerNodeCount(maxPerNodeCnt);
     *
     *     ignite.services().deploy(cfg);
     * </pre>
     *
     * @param name Service name.
     * @param svc Service instance.
     * @param totalCnt Maximum number of deployed services in the grid, {@code 0} for unlimited.
     * @param maxPerNodeCnt Maximum number of deployed services on each node, {@code 0} for unlimited.
     * @throws ServiceDeploymentException If failed to deploy service.
     */
    @IgniteAsyncSupported
    public void deployMultiple(String name, Service svc, int totalCnt, int maxPerNodeCnt)
        throws ServiceDeploymentException;

    /**
     * Asynchronously deploys multiple instances of the service on the grid. Ignite will deploy a
     * maximum amount of services equal to {@code 'totalCnt'} parameter making sure that
     * there are no more than {@code 'maxPerNodeCnt'} service instances running
     * on each node. Whenever topology changes, Ignite will automatically rebalance
     * the deployed services within cluster to make sure that each node will end up with
     * about equal number of deployed instances whenever possible.
     * <p>
     * Note that at least one of {@code 'totalCnt'} or {@code 'maxPerNodeCnt'} parameters must have
     * value greater than {@code 0}.
     * <p>
     * This method is analogous to the invocation of
     * {@link #deployAsync(org.apache.ignite.services.ServiceConfiguration)} method as follows:
     * <pre name="code" class="java">
     *     ServiceConfiguration cfg = new ServiceConfiguration();
     *
     *     cfg.setName(name);
     *     cfg.setService(svc);
     *     cfg.setTotalCount(totalCnt);
     *     cfg.setMaxPerNodeCount(maxPerNodeCnt);
     *
     *     ignite.services().deployAsync(cfg);
     * </pre>
     *
     * @param name Service name.
     * @param svc Service instance.
     * @param totalCnt Maximum number of deployed services in the grid, {@code 0} for unlimited.
     * @param maxPerNodeCnt Maximum number of deployed services on each node, {@code 0} for unlimited.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> deployMultipleAsync(String name, Service svc, int totalCnt, int maxPerNodeCnt);

    /**
     * Deploys multiple instances of the service on the grid according to provided
     * configuration. Ignite will deploy a maximum amount of services equal to
     * {@link org.apache.ignite.services.ServiceConfiguration#getTotalCount() cfg.getTotalCount()}  parameter
     * making sure that there are no more than
     * {@link org.apache.ignite.services.ServiceConfiguration#getMaxPerNodeCount() cfg.getMaxPerNodeCount()}
     * service instances running on each node. Whenever topology changes, Ignite will automatically rebalance
     * the deployed services within cluster to make sure that each node will end up with
     * about equal number of deployed instances whenever possible.
     * <p>
     * If {@link org.apache.ignite.services.ServiceConfiguration#getAffinityKey() cfg.getAffinityKey()}
     * is not {@code null}, then Ignite  will deploy the service on the primary node for given affinity key.
     * The affinity will be calculated on the cache with
     * {@link org.apache.ignite.services.ServiceConfiguration#getCacheName() cfg.getCacheName()} name.
     * <p>
     * If {@link org.apache.ignite.services.ServiceConfiguration#getNodeFilter() cfg.getNodeFilter()}
     * is not {@code null}, then  Ignite will deploy service on all grid nodes for which
     * the provided filter evaluates to {@code true}.
     * The node filter will be checked in addition to the underlying cluster group filter, or the
     * whole grid, if the underlying cluster group includes all the cluster nodes.
     * <p>
     * Note that at least one of {@code 'totalCnt'} or {@code 'maxPerNodeCnt'} parameters must have
     * value greater than {@code 0}.
     * <p>
     * Here is an example of creating service deployment configuration:
     * <pre name="code" class="java">
     *     ServiceConfiguration cfg = new ServiceConfiguration();
     *
     *     cfg.setName(name);
     *     cfg.setService(svc);
     *     cfg.setTotalCount(0); // Unlimited.
     *     cfg.setMaxPerNodeCount(2); // Deploy 2 instances of service on each node.
     *
     *     ignite.services().deploy(cfg);
     * </pre>
     *
     * @param cfg Service configuration.
     * @throws ServiceDeploymentException If failed to deploy service.
     */
    @IgniteAsyncSupported
    public void deploy(ServiceConfiguration cfg) throws ServiceDeploymentException;

    /**
     * Asynchronously deploys multiple instances of the service on the grid according to provided
     * configuration. Ignite will deploy a maximum amount of services equal to
     * {@link org.apache.ignite.services.ServiceConfiguration#getTotalCount() cfg.getTotalCount()}  parameter
     * making sure that there are no more than
     * {@link org.apache.ignite.services.ServiceConfiguration#getMaxPerNodeCount() cfg.getMaxPerNodeCount()}
     * service instances running on each node. Whenever topology changes, Ignite will automatically rebalance
     * the deployed services within cluster to make sure that each node will end up with
     * about equal number of deployed instances whenever possible.
     * <p>
     * If {@link org.apache.ignite.services.ServiceConfiguration#getAffinityKey() cfg.getAffinityKey()}
     * is not {@code null}, then Ignite
     * will deploy the service on the primary node for given affinity key. The affinity will be calculated
     * on the cache with {@link org.apache.ignite.services.ServiceConfiguration#getCacheName() cfg.getCacheName()} name.
     * <p>
     * If {@link org.apache.ignite.services.ServiceConfiguration#getNodeFilter() cfg.getNodeFilter()}
     * is not {@code null}, then Ignite will deploy service on all grid nodes
     * for which the provided filter evaluates to {@code true}.
     * The node filter will be checked in addition to the underlying cluster group filter, or the
     * whole grid, if the underlying cluster group includes all the cluster nodes.
     * <p>
     * Note that at least one of {@code 'totalCnt'} or {@code 'maxPerNodeCnt'} parameters must have
     * value greater than {@code 0}.
     * <p>
     * Here is an example of creating service deployment configuration:
     * <pre name="code" class="java">
     *     ServiceConfiguration cfg = new ServiceConfiguration();
     *
     *     cfg.setName(name);
     *     cfg.setService(svc);
     *     cfg.setTotalCount(0); // Unlimited.
     *     cfg.setMaxPerNodeCount(2); // Deploy 2 instances of service on each node.
     *
     *     ignite.services().deployAsync(cfg);
     * </pre>
     *
     * @param cfg Service configuration.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> deployAsync(ServiceConfiguration cfg);

    /**
     * Deploys multiple services described by provided configurations. Depending on specified parameters, multiple
     * instances of the same service may be deployed (see {@link ServiceConfiguration}). Whenever topology changes,
     * Ignite will automatically rebalance the deployed services within cluster to make sure that each node will end up
     * with about equal number of deployed instances whenever possible.
     *
     * If deployment of some of the provided services fails, then {@link ServiceDeploymentException} containing a list
     * of failed services will be thrown. It is guaranteed that all services that were provided to this method and are
     * not present in the list of failed services are successfully deployed by the moment of the exception being thrown.
     * Note that if exception is thrown, then partial deployment may have occurred.
     *
     * @param cfgs {@link Collection} of service configurations to be deployed.
     * @throws ServiceDeploymentException If failed to deploy services.
     * @see IgniteServices#deploy(ServiceConfiguration)
     * @see IgniteServices#deployAllAsync(Collection)
     */
    public void deployAll(Collection<ServiceConfiguration> cfgs) throws ServiceDeploymentException;

    /**
     * Asynchronously deploys multiple services described by provided configurations. Depending on specified parameters,
     * multiple instances of the same service may be deployed (see {@link ServiceConfiguration}). Whenever topology
     * changes, Ignite will automatically rebalance the deployed services within cluster to make sure that each node
     * will end up with about equal number of deployed instances whenever possible.
     *
     * If deployment of some of the provided services fails, then {@link ServiceDeploymentException} containing a list
     * of failed services will be thrown from {@link IgniteFuture#get get()} method of the returned future. It is
     * guaranteed that all services, that were provided to this method and are not present in the list of failed
     * services, are successfully deployed by the moment of the exception being thrown. Note that if exception is
     * thrown, then partial deployment may have occurred.
     *
     * @param cfgs {@link Collection} of service configurations to be deployed.
     * @return a Future representing pending completion of the operation.
     * @see IgniteServices#deploy(ServiceConfiguration)
     * @see IgniteServices#deployAll(Collection)
     */
    public IgniteFuture<Void> deployAllAsync(Collection<ServiceConfiguration> cfgs);

    /**
     * Cancels service deployment. If a service with specified name was deployed on the grid,
     * then {@link org.apache.ignite.services.Service#cancel(org.apache.ignite.services.ServiceContext)}
     * method will be called on it.
     * <p>
     * Note that Ignite cannot guarantee that the service exits from
     * {@link org.apache.ignite.services.Service#execute(org.apache.ignite.services.ServiceContext)}
     * method whenever {@link org.apache.ignite.services.Service#cancel(org.apache.ignite.services.ServiceContext)}
     * is called. It is up to the user to  make sure that the service code properly reacts to cancellations.
     * <p>
     * Supports asynchronous execution (see {@link IgniteAsyncSupport}).
     *
     * @param name Name of service to cancel.
     * @throws IgniteException If failed to cancel service.
     */
    @IgniteAsyncSupported
    public void cancel(String name) throws IgniteException;

    /**
     * Asynchronously cancels service deployment. If a service with specified name was deployed on the grid,
     * then {@link org.apache.ignite.services.Service#cancel(org.apache.ignite.services.ServiceContext)}
     * method will be called on it.
     * <p>
     * Note that Ignite cannot guarantee that the service exits from
     * {@link org.apache.ignite.services.Service#execute(org.apache.ignite.services.ServiceContext)}
     * method whenever {@link org.apache.ignite.services.Service#cancel(org.apache.ignite.services.ServiceContext)}
     * is called. It is up to the user to
     * make sure that the service code properly reacts to cancellations.
     *
     * @param name Name of service to cancel.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> cancelAsync(String name);

    /**
     * Cancels services with specified names.
     * <p>
     * Note that depending on user logic, it may still take extra time for a service to
     * finish execution, even after it was cancelled.
     * <p>
     * Supports asynchronous execution (see {@link IgniteAsyncSupport}).
     *
     * @param names Names of services to cancel.
     * @throws IgniteException If failed to cancel services.
     */
    @IgniteAsyncSupported
    public void cancelAll(Collection<String> names) throws IgniteException;

    /**
     * Asynchronously cancels services with specified names.
     * <p>
     * Note that depending on user logic, it may still take extra time for a service to
     * finish execution, even after it was cancelled.
     *
     * @param names Names of services to cancel.
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> cancelAllAsync(Collection<String> names);

    /**
     * Cancels all deployed services.
     * <p>
     * Note that depending on user logic, it may still take extra time for a service to
     * finish execution, even after it was cancelled.
     * <p>
     * Supports asynchronous execution (see {@link IgniteAsyncSupport}).
     *
     * @throws IgniteException If failed to cancel services.
     */
    @IgniteAsyncSupported
    public void cancelAll() throws IgniteException;

    /**
     * Asynchronously cancels all deployed services.
     * <p>
     * Note that depending on user logic, it may still take extra time for a service to
     * finish execution, even after it was cancelled.
     *
     * @return a Future representing pending completion of the operation.
     */
    public IgniteFuture<Void> cancelAllAsync();

    /**
     * Gets metadata about all deployed services in the grid.
     *
     * @return Metadata about all deployed services in the grid.
     */
    public Collection<ServiceDescriptor> serviceDescriptors();

    /**
     * Gets locally deployed service with specified name.
     *
     * @param name Service name.
     * @param <T> Service type
     * @return Deployed service with specified name.
     * @see ServiceConfiguration#setStatisticsEnabled(boolean)
     * @deprecated Use the proxies like {@link #serviceProxy(String, Class, boolean)}. References to local services
     * corrupt the service statistics and bring no real performance optimization.
     */
    @Deprecated
    public <T> T service(String name);

    /**
     * Gets all locally deployed services with specified name.
     *
     * @param name Service name.
     * @param <T> Service type.
     * @return all deployed services with specified name.
     * @see ServiceConfiguration#setStatisticsEnabled(boolean)
     * @deprecated Use the proxies like {@link #serviceProxy(String, Class, boolean)}. References to local services
     * corrupt the service statistics and bring no real performance optimization.
     */
    @Deprecated
    public <T> Collection<T> services(String name);

    /**
     * Gets a handle on remote or local service. The proxy is dynamically created and provided for the specified service.
     *
     * @param name Service name.
     * @param svcItf Interface for the service.
     * @param sticky Whether or not Ignite should always contact the same remote
     *      service or try to load-balance between services.
     * @param <T> Service type.
     * @return Proxy over service.
     * @throws IgniteException If failed to create service proxy.
     */
    public <T> T serviceProxy(String name, Class<? super T> svcItf, boolean sticky) throws IgniteException;

    /**
     * Gets a handle on remote or local service with the timeout. The proxy is dynamically created and provided for the
     * specified service.
     *
     * @param name Service name.
     * @param svcItf Interface for the service.
     * @param sticky Whether or not Ignite should always contact the same remote
     *      service or try to load-balance between services.
     * @param timeout If greater than 0 created proxy will wait for service availability only specified time,
     *  and will limit remote service invocation time.
     * @param <T> Service type.
     * @return Proxy over service.
     * @throws IgniteException If failed to create service proxy.
     */
    public <T> T serviceProxy(String name, Class<? super T> svcItf, boolean sticky, long timeout)
        throws IgniteException;

    /**
     * Gets a handle on remote or local service with the specified caller context. The proxy is dynamically created and
     * provided for the specified service.
     *
     * @param name Service name.
     * @param svcItf Interface for the service.
     * @param sticky Whether or not Ignite should always contact the same remote
     *      service or try to load-balance between services.
     * @param callCtx Service call context.
     * @param <T> Service type.
     * @return Proxy over service.
     * @throws IgniteException If failed to create service proxy.
     * @see ServiceCallContext
     */
    @IgniteExperimental
    public <T> T serviceProxy(
        String name,
        Class<? super T> svcItf,
        boolean sticky,
        ServiceCallContext callCtx
    ) throws IgniteException;

    /**
     * Gets a handle on remote or local service with the specified caller context and the timeout. The proxy is
     * dynamically created and provided for the specified service.
     *
     * @param name Service name.
     * @param svcItf Interface for the service.
     * @param sticky Whether or not Ignite should always contact the same remote
     *      service or try to load-balance between services.
     * @param callCtx Service call context.
     * @param timeout If greater than 0 created proxy will wait for service availability only specified time,
     *  and will limit remote service invocation time.
     * @param <T> Service type.
     * @return Proxy over service.
     * @throws IgniteException If failed to create service proxy.
     * @see ServiceCallContext
     */
    @IgniteExperimental
    public <T> T serviceProxy(
        String name,
        Class<? super T> svcItf,
        boolean sticky,
        ServiceCallContext callCtx,
        long timeout
    ) throws IgniteException;

    /** {@inheritDoc} */
    @Deprecated
    @Override public IgniteServices withAsync();
}
