eturn classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();
        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }
        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            r

        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);

            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();
        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }
        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();
        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }
        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {

                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


@Override
    public boolean checkEventType(GameEvent event, Game game)

package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

{
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {
        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }
        Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
        if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }
        return permanent != null && permanent.isCreature(game) && !permanent.isControlledBy(getControllerId());
    }

    @Override
    public GenerousPatronTriggeredAbility copy() {
        return new GenerousPatronTriggeredAbility(this);
    }

    @Override
    public String getTriggerPhrase() {
        return "Whenever you put one or more counters on a creature you don't control, ";
    }
}


{
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

    @Override
    public boolean checkEventType(GameEvent event, Game game)

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {
        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }
        Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
        if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }
        return permanent != null && permanent.isCreature(game) && !permanent.isControlledBy(getControllerId());
    }

    @Override
    public GenerousPatronTriggeredAbility copy() {
        return new GenerousPatronTriggeredAbility(this);
    }

    @Override
    public String getTriggerPhrase() {
        return "Whenever you put one or more counters on a creature you don't control, ";
    }
}


        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }

package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

    @Override
    public boolean checkEventType(GameEvent event, Game game) {
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {

        Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
        if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }
        return permanent != null && permanent.isCreature(game) && !permanent.isControlledBy(getControllerId());
    }

    @Override
    public GenerousPatronTriggeredAbility copy() {
        return new GenerousPatronTriggeredAbility(this);
    }

    @Override
    public String getTriggerPhrase() {
        return "Whenever you put one or more counters on a creature you don't control, ";
    }
}


Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
        if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }

package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

    @Override
    public boolean checkEventType(GameEvent event, Game game) {
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {
        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }

        return permanent != null && permanent.isCreature(game) && !permanent.isControlledBy(getControllerId());
    }

    @Override
    public GenerousPatronTriggeredAbility copy() {
        return new GenerousPatronTriggeredAbility(this);
    }

    @Override
    public String getTriggerPhrase() {
        return "Whenever you put one or more counters on a creature you don't control, ";
    }
}


return permanent != null && permanent.isCreature(game) && !permanent.isControlledBy(getControllerId());
    }

package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

    @Override
    public boolean checkEventType(GameEvent event, Game game) {
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {
        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }
        Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
        if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }

    @Override
    public GenerousPatronTriggeredAbility copy() {
        return new GenerousPatronTriggeredAbility(this);
    }

    @Override
    public String getTriggerPhrase() {
        return "Whenever you put one or more counters on a creature you don't control, ";
    }
}


package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

    @Override
    public boolean checkEventType(GameEvent event, Game game) {
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {
        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }
        Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
        if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }
        return permanent != null && permanent.isCreature(game) && !permanent.isControlledBy(getControllerId());
    }

    @Override
    public GenerousPatronTriggeredAbility copy() {
        return new GenerousPatronTriggeredAbilit

    }

    @Override
    public String getTriggerPhrase() {
        return "Whenever you put one or more counters on a creature you don't control, ";
    }
}


if (permanent == null) {
            permanent = game.getPermanentEntering(event.getTargetId());
        }

package mage.cards.g;

import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.SupportAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;

/**
 * @author TheElk801
 */
public final class GenerousPatron extends CardImpl {

    public GenerousPatron(UUID ownerId, CardSetInfo setInfo) {
        super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{2}{G}");
        this.subtype.add(SubType.ELF);
        this.subtype.add(SubType.ADVISOR);
        this.power = new MageInt(1);
        this.toughness = new MageInt(4);
        // When Generous Patron enters the battlefield, support 2. (Put a +1/+1 counter on each of up to two other target creatures.)
        this.addAbility(new SupportAbility(this, 2));
        // Whenever you put one or more counters on a creature you don't control, draw a card.
        this.addAbility(new GenerousPatronTriggeredAbility());
    }

    private GenerousPatron(final GenerousPatron card) {
        super(card);
    }

    @Override
    public GenerousPatron copy() {
        return new GenerousPatron(this);
    }
}

class GenerousPatronTriggeredAbility extends TriggeredAbilityImpl {

    GenerousPatronTriggeredAbility() {
        super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
    }

    private GenerousPatronTriggeredAbility(GenerousPatronTriggeredAbility ability) {
        super(ability);
    }

    @Override
    public boolean checkEventType(GameEvent event, Game game) {
        return event.getType() == GameEvent.EventType.COUNTERS_ADDED;
    }

    @Override
    public boolean checkTrigger(GameEvent event, Game game) {
        if (!isControlledBy(event.getPlayerId())) {
            return false;
        }
        Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());


package com.google.android.gms.common.internal;

import android.accounts.Account;
import android.os.Bundle;
import android.os.IBinder;
import com.google.android.gms.common.api.Scope;
import org.microg.gms.common.Constants;
import org.microg.gms.common.GmsService;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
import java.util.Arrays;

public class GetServiceRequest extends AutoSafeParcelable {

    @SafeParceled(1)
    private int versionCode = 3;

    @SafeParceled(2)
    public final int serviceId;

    @SafeParceled(3)
    public int gmsVersion;

    @SafeParceled(4)
    public String packageName;

    @SafeParceled(5)
    public IBinder accountAccessor;

    @SafeParceled(6)
    public Scope[] scopes;

    @SafeParceled(7)
    public Bundle extras;

    @SafeParceled(8)
    public Account account;

    @SafeParceled(9)
    public long long1;

    private GetServiceRequest() {
        serviceId = -1;
        gmsVersion = Constants.MAX_REFERENCE_VERSION;
    }

    public GetServiceRequest(int serviceId) {
        this.serviceId = serviceId;
        this.gmsVersion = Constants.MAX_REFERENCE_VERSION;
    }

{
        return "GetServiceRequest{" + "serviceId=" + GmsService.nameFromServiceId(serviceId) + ", gmsVersion=" + gmsVersion + ", packageName='" + packageName + '\'' + (scopes == null || scopes.length == 0 ? "" : (", scopes=" + Arrays.toString(scopes))) + (extras == null ? "" : (", extras=" + extras)) + (account == null ? "" : (", account=" + account)) + '}';
    }

    public static Creator<GetServiceRequest> CREATOR = new AutoCreator<GetServiceRequest>(GetServiceRequest.class);
}


{
        return "GetServiceRequest{" + "serviceId=" + GmsService.nameFromServiceId(serviceId) + ", gmsVersion=" + gmsVersion + ", packageName='" + packageName + '\'' + (scopes == null || scopes.length == 0 ? "" : (", scopes=" + Arrays.toString(scopes))) + (extras == null ? "" : (", extras=" + extras)) + (account == null ? "" : (", account=" + account)) + '}';
    }

package com.google.android.gms.common.internal;

import android.accounts.Account;
import android.os.Bundle;
import android.os.IBinder;
import com.google.android.gms.common.api.Scope;
import org.microg.gms.common.Constants;
import org.microg.gms.common.GmsService;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
import java.util.Arrays;

public class GetServiceRequest extends AutoSafeParcelable {

    @SafeParceled(1)
    private int versionCode = 3;

    @SafeParceled(2)
    public final int serviceId;

    @SafeParceled(3)
    public int gmsVersion;

    @SafeParceled(4)
    public String packageName;

    @SafeParceled(5)
    public IBinder accountAccessor;

    @SafeParceled(6)
    public Scope[] scopes;

    @SafeParceled(7)
    public Bundle extras;

    @SafeParceled(8)
    public Account account;

    @SafeParceled(9)
    public long long1;

    private GetServiceRequest() {
        serviceId = -1;
        gmsVersion = Constants.MAX_REFERENCE_VERSION;
    }

    public GetServiceRequest(int serviceId) {
        this.serviceId = serviceId;
        this.gmsVersion = Constants.MAX_REFERENCE_VERSION;
    }

    @Override
    public String toString()

    public static Creator<GetServiceRequest> CREATOR = new AutoCreator<GetServiceRequest>(GetServiceRequest.class);
}


Version + ", packageName='" + packageName + '\'' + (scopes == null || scopes.length == 0 ? "" : (", scopes=" + Arrays.toString(scopes))) + (extras == null ? "" : (", extras=" + extras)) + (account == null ? "" : (", account=" + account)) + '}';

package com.google.android.gms.common.internal;

import android.accounts.Account;
import android.os.Bundle;
import android.os.IBinder;
import com.google.android.gms.common.api.Scope;
import org.microg.gms.common.Constants;
import org.microg.gms.common.GmsService;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
import java.util.Arrays;

public class GetServiceRequest extends AutoSafeParcelable {

    @SafeParceled(1)
    private int versionCode = 3;

    @SafeParceled(2)
    public final int serviceId;

    @SafeParceled(3)
    public int gmsVersion;

    @SafeParceled(4)
    public String packageName;

    @SafeParceled(5)
    public IBinder accountAccessor;

    @SafeParceled(6)
    public Scope[] scopes;

    @SafeParceled(7)
    public Bundle extras;

    @SafeParceled(8)
    public Account account;

    @SafeParceled(9)
    public long long1;

    private GetServiceRequest() {
        serviceId = -1;
        gmsVersion = Constants.MAX_REFERENCE_VERSION;
    }

    public GetServiceRequest(int serviceId) {
        this.serviceId = serviceId;
        this.gmsVersion = Constants.MAX_REFERENCE_VERSION;
    }

    @Override
    public String toString() {
        return "GetServiceRequest{" + "serviceId=" + GmsService.nameFromServiceId(serviceId) + ", gmsVersion=" + gms

    }

    public static Creator<GetServiceRequest> CREATOR = new AutoCreator<GetServiceRequest>(GetServiceRequest.class);
}


package org.apache.ignite.internal.mem;

import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;

public class UnsafeChunk implements DirectMemoryRegion {

    private long ptr;

    private long len;

    /**
     * @param ptr Pointer to the memory start.
     * @param len Memory length.
     */
    public UnsafeChunk(long ptr, long len) {
        this.ptr = ptr;
        this.len = len;
    }

    @Override
    public long address() {
        return ptr;
    }

    @Override
    public long size() {
        return len;
    }

    @Override
    public DirectMemoryRegion slice(long offset) {
        if (offset < 0 || offset >= len)
            throw new IllegalArgumentException("Failed to create a memory region slice [ptr=" + U.hexLong(ptr) + ", len=" + len + ", offset=" + offset + ']');
        return new UnsafeChunk(ptr + offset, len - offset);
    }

{
        return S.toString(UnsafeChunk.class, this);
    }
}


{
        if (offset < 0 || offset >= len)
            throw new IllegalArgumentException("Failed to create a memory region slice [ptr=" + U.hexLong(ptr) + ", len=" + len + ", offset=" + offset + ']');
        return new UnsafeChunk(ptr + offset, len - offset);
    }

package org.apache.ignite.internal.mem;

import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;

public class UnsafeChunk implements DirectMemoryRegion {

    private long ptr;

    private long len;

    /**
     * @param ptr Pointer to the memory start.
     * @param len Memory length.
     */
    public UnsafeChunk(long ptr, long len) {
        this.ptr = ptr;
        this.len = len;
    }

    @Override
    public long address() {
        return ptr;
    }

    @Override
    public long size() {
        return len;
    }

    @Override
    public DirectMemoryRegion slice(long offset)

    @Override
    public String toString() {
        return S.toString(UnsafeChunk.class, this);
    }
}


package org.apache.ignite.internal.mem;

import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;

public class UnsafeChunk implements DirectMemoryRegion {

    private long ptr;

    private long len;

    /**
     * @param ptr Pointer to the memory start.
     * @param len Memory length.
     */
    public UnsafeChunk(long ptr, long len) {
        this.ptr = ptr;
        this.len = len;
    }

    @Override
    public long address() {
        return ptr;
    }

    @Override
    public long size() {
        return len;
    }

    @Override
    public DirectMemoryRegion slice(long offset) {
        if (offset < 0 || offset >= len)
            throw new IllegalArgumentException("Failed to create a memory region slice [ptr=" + U.hexLong(ptr) + ", len=" + len + ", offset=" + offset + ']');
        return new UnsafeChunk(ptr + off

    }

    @Override
    public String toString() {
        return S.toString(UnsafeChunk.class, this);
    }
}


if (offset < 0 || offset >= len)
            throw new IllegalArgumentException("Failed to create a memory region slice [ptr=" + U.hexLong(ptr) + ", len=" + len + ", offset=" + offset + ']');

package org.apache.ignite.internal.mem;

import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;

public class UnsafeChunk implements DirectMemoryRegion {

    private long ptr;

    private long len;

    /**
     * @param ptr Pointer to the memory start.
     * @param len Memory length.
     */
    public UnsafeChunk(long ptr, long len) {
        this.ptr = ptr;
        this.len = len;
    }

    @Override
    public long address() {
        return ptr;
    }

    @Override
    public long size() {
        return len;
    }

    @Override
    public DirectMemoryRegion slice(long offset) {

        return new UnsafeChunk(ptr + offset, len - offset);
    }

    @Override
    public String toString() {
        return S.toString(UnsafeChunk.class, this);
    }
}


@Bean
    public DomainEventDispatcher passengerServiceEventDispatcher(final PassengerServiceEventConsumer passengerServiceEventConsumer, final DomainEventDispatcherFactory domainEventDispatcherFactory)

package io.vividcode.happyride.passengerservice.messagehandlers;

import io.eventuate.tram.consumer.common.DuplicateMessageDetector;
import io.eventuate.tram.consumer.common.NoopDuplicateMessageDetector;
import io.eventuate.tram.events.subscriber.DomainEventDispatcher;
import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory;
import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration;
import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration;
import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({ TramJdbcKafkaConfiguration.class, TramEventsPublisherConfiguration.class, TramEventSubscriberConfiguration.class })
public class PassengerServiceMessageHandlersConfiguration {

    @Bean
    public PassengerServiceEventConsumer passengerServiceEventConsumer() {
        return new PassengerServiceEventConsumer();
    }

{
        return domainEventDispatcherFactory.make("passengerServiceEvents", passengerServiceEventConsumer.domainEventHandlers());
    }

    @Bean
    public DuplicateMessageDetector duplicateMessageDetector() {
        return new NoopDuplicateMessageDetector();
    }
}


{
        return domainEventDispatcherFactory.make("passengerServiceEvents", passengerServiceEventConsumer.domainEventHandlers());
    }

package io.vividcode.happyride.passengerservice.messagehandlers;

import io.eventuate.tram.consumer.common.DuplicateMessageDetector;
import io.eventuate.tram.consumer.common.NoopDuplicateMessageDetector;
import io.eventuate.tram.events.subscriber.DomainEventDispatcher;
import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory;
import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration;
import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration;
import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({ TramJdbcKafkaConfiguration.class, TramEventsPublisherConfiguration.class, TramEventSubscriberConfiguration.class })
public class PassengerServiceMessageHandlersConfiguration {

    @Bean
    public PassengerServiceEventConsumer passengerServiceEventConsumer() {
        return new PassengerServiceEventConsumer();
    }

    @Bean
    public DomainEventDispatcher passengerServiceEventDispatcher(final PassengerServiceEventConsumer passengerServiceEventConsumer, final DomainEventDispatcherFactory domainEventDispatcherFactory)

    @Bean
    public DuplicateMessageDetector duplicateMessageDetector() {
        return new NoopDuplicateMessageDetector();
    }
}


package io.vividcode.happyride.passengerservice.messagehandlers;

import io.eventuate.tram.consumer.common.DuplicateMessageDetector;
import io.eventuate.tram.consumer.common.NoopDuplicateMessageDetector;
import io.eventuate.tram.events.subscriber.DomainEventDispatcher;
import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory;
import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration;
import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration;
import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({ TramJdbcKafkaConfiguration.class, TramEventsPublisherConfiguration.class, TramEventSubscriberConfiguration.class })
public class PassengerServiceMessageHandlersConfiguration {

    @Bean
    public PassengerServiceEventConsumer passengerServiceEventConsumer() {
        return new PassengerServiceEventConsumer();
    }

    @Bean
    public DomainEventDispatcher passengerServiceEventDispatcher(final PassengerServiceEventConsumer passengerServiceEventConsumer, final DomainEventDispatcherFactory domainEventDispatcherFactory) {
        return domainEventDispatcherFactory.make("passengerServiceEvents", passengerServiceEventConsumer.domainEventHandlers());
    }

    @Bean
    public DuplicateMessageDetector duplicateMessageDetector() {
        return new No


package coding.challenge;

public class Main {

{
        /* This tree is BST
              40
            /    \
          35      45
         /  \    /  \
        30   37 44   48
       /
      20
    */
        BinaryTree<Integer> bt = new BinaryTree<>();
        bt.insert(40);
        bt.insert(35);
        bt.insert(45);
        bt.insert(30);
        bt.insert(37);
        bt.insert(44);
        bt.insert(48);
        bt.insert(20);
        System.out.println("\n\nTraversal LEVEL: ");
        bt.print();
        System.out.println("\n\nIs this binary tree BST: " + bt.isBinarySearchTree());
    }
}


        /* This tree is BST
              40
            /    \
          35      45
         /  \    /  \
        30   37 44   48
       /
      20
    */
        BinaryTree<Integer> bt = new BinaryTree<>();
        bt.insert(40);
        bt.insert(35);

package coding.challenge;

public class Main {

    public static void main(String[] args) {

        bt.insert(45);
        bt.insert(30);
        bt.insert(37);
        bt.insert(44);
        bt.insert(48);
        bt.insert(20);
        System.out.println("\n\nTraversal LEVEL: ");
        bt.print();
        System.out.println("\n\nIs this binary tree BST: " + bt.isBinarySearchTree());
    }
}


bt.insert(45);
        bt.insert(30);
        bt.insert(37);
        bt.insert(44);
        bt.insert(48);

package coding.challenge;

public class Main {

    public static void main(String[] args) {
        /* This tree is BST
              40
            /    \
          35      45
         /  \    /  \
        30   37 44   48
       /
      20
    */
        BinaryTree<Integer> bt = new BinaryTree<>();
        bt.insert(40);
        bt.insert(35);

        bt.insert(20);
        System.out.println("\n\nTraversal LEVEL: ");
        bt.print();
        System.out.println("\n\nIs this binary tree BST: " + bt.isBinarySearchTree());
    }
}


bt.insert(20);
        System.out.println("\n\nTraversal LEVEL: ");
        bt.print();
        System.out.println("\n\nIs this binary tree BST: " + bt.isBinarySearchTree());
    }

package coding.challenge;

public class Main {

    public static void main(String[] args) {
        /* This tree is BST
              40
            /    \
          35      45
         /  \    /  \
        30   37 44   48
       /
      20
    */
        BinaryTree<Integer> bt = new BinaryTree<>();
        bt.insert(40);
        bt.insert(35);
        bt.insert(45);
        bt.insert(30);
        bt.insert(37);
        bt.insert(44);
        bt.insert(48);


package coding.challenge;

public class Main {

    public static void main(String[] args) {
        /* This tree is BST
              40
            /    \
          35      45
         /  \    /  \
        30   37 44   48
       /
      20
    */
        BinaryTree<Integer> bt = new BinaryTree<>();
        bt.insert(40);
        bt.insert(35);
        bt.ins

        bt.insert(30);
        bt.insert(37);
        bt.insert(44);
        bt.insert(48);
        bt.insert(20);
        System.out.println("\n\nTraversal LEVEL: ");
        bt.print();
        System.out.println("\n\nIs this binary tree BST: " + bt.isBinarySearchTree());
    }
}


@Override
    protected void writeExternalData(ObjectOutput out) throws IOException

package org.apache.ignite.internal.visor.query;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.VisorDataTransferObject;

/**
 * Arguments for {@link VisorQueryTask}.
 */
public class VisorQueryTaskArg extends VisorDataTransferObject {

    private static final long serialVersionUID = 0L;

    /**
     * Cache name for query.
     */
    private String cacheName;

    /**
     * Query text.
     */
    private String qryTxt;

    /**
     * Distributed joins enabled flag.
     */
    private boolean distributedJoins;

    /**
     * Enforce join order flag.
     */
    private boolean enforceJoinOrder;

    /**
     * Query contains only replicated tables flag.
     */
    private boolean replicatedOnly;

    /**
     * Flag whether to execute query locally.
     */
    private boolean loc;

    /**
     * Result batch size.
     */
    private int pageSize;

    /**
     * Lazy query execution flag
     */
    private boolean lazy;

    /**
     * Collocation flag.
     */
    private boolean collocated;

    public VisorQueryTaskArg() {
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, false, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, lazy, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     * @param collocated Collocation flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy, boolean collocated) {
        this.cacheName = cacheName;
        this.qryTxt = qryTxt;
        this.distributedJoins = distributedJoins;
        this.enforceJoinOrder = enforceJoinOrder;
        this.replicatedOnly = replicatedOnly;
        this.loc = loc;
        this.pageSize = pageSize;
        this.lazy = lazy;
        this.collocated = collocated;
    }

    public String getCacheName() {
        return cacheName;
    }

    public String getQueryText() {
        return qryTxt;
    }

    /**
     * @return Distributed joins enabled flag.
     */
    public boolean isDistributedJoins() {
        return distributedJoins;
    }

    /**
     * @return Enforce join order flag.
     */
    public boolean isEnforceJoinOrder() {
        return enforceJoinOrder;
    }

    /**
     * @return {@code true} If the query contains only replicated tables.
     */
    public boolean isReplicatedOnly() {
        return replicatedOnly;
    }

    /**
     * @return {@code true} If query should be executed locally.
     */
    public boolean isLocal() {
        return loc;
    }

    public int getPageSize() {
        return pageSize;
    }

    /**
     * Lazy query execution flag.
     *
     * @return Lazy flag.
     */
    public boolean getLazy() {
        return lazy;
    }

    /**
     * Flag indicating if this query is collocated.
     *
     * @return {@code true} If the query is collocated.
     */
    public boolean isCollocated() {
        return collocated;
    }

    @Override
    public byte getProtocolVersion() {
        return V3;
    }

{
        U.writeString(out, cacheName);
        U.writeString(out, qryTxt);
        out.writeBoolean(distributedJoins);
        out.writeBoolean(enforceJoinOrder);
        out.writeBoolean(loc);
        out.writeInt(pageSize);
        out.writeBoolean(lazy);
        out.writeBoolean(collocated);
    }

    @Override
    protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException {
        cacheName = U.readString(in);
        qryTxt = U.readString(in);
        distributedJoins = in.readBoolean();
        enforceJoinOrder = in.readBoolean();
        loc = in.readBoolean();
        pageSize = in.readInt();
        if (protoVer > V1)
            lazy = in.readBoolean();
        if (protoVer > V2)
            collocated = in.readBoolean();
    }

    @Override
    public String toString() {
        return S.toString(VisorQueryTaskArg.class, this);
    }
}


package org.apache.ignite.internal.visor.query;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.VisorDataTransferObject;

/**
 * Arguments for {@link VisorQueryTask}.
 */
public class VisorQueryTaskArg extends VisorDataTransferObject {

    private static final long serialVersionUID = 0L;

    /**
     * Cache name for query.
     */
    private String cacheName;

    /**
     * Query text.
     */
    private String qryTxt;

    /**
     * Distributed joins enabled flag.
     */
    private boolean distributedJoins;

    /**
     * Enforce join order flag.
     */
    private boolean enforceJoinOrder;

    /**
     * Query contains only replicated tables flag.
     */
    private boolean replicatedOnly;

    /**
     * Flag whether to execute query locally.
     */
    private boolean loc;

    /**
     * Result batch size.
     */
    private int pageSize;

    /**
     * Lazy query execution flag
     */
    private boolean lazy;

    /**
     * Collocation flag.
     */
    private boolean collocated;

    public VisorQueryTaskArg() {
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, false, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, lazy, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     * @param collocated Collocation flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy, boolean collocated) {
        this.cacheName = cacheName;
        this.qryTxt = qryTxt;
        this.distributedJoins = distributedJoins;
        this.enforceJoinOrder = enforceJoinOrder;
        this.replicatedOnly = replicatedOnly;
        this.loc = loc;
        this.pageSize = pageSize;
        this.lazy = lazy;
        this.collocated = collocated;
    }

    public String getCacheName() {
        return cacheName;
    }

    public String getQueryText() {
        return qryTxt;
    }

    /**
     * @return Distributed joins enabled flag.
     */
    public boolean isDistributedJoins() {
        return distributedJoins;
    }

    /**
     * @return Enforce join order flag.
     */
    public boolean isEnforceJoinOrder() {
        return enforceJoinOrder;
    }

    /**
     * @return {@code true} If the query contains only replicated tables.
     */
    public boolean isReplicatedOnly() {
        return replicatedOnly;
    }

    /**
     * @return {@code true} If query should be executed locally.
     */
    public boolean isLocal() {
        return loc;
    }

    public int getPageSize() {
        return pageSize;
    }

    /**
     * Lazy query execution flag.
     *
     * @return Lazy flag.
     */
    public boolean getLazy()

    /**
     * Flag indicating if this query is collocated.
     *
     * @return {@code true} If the query is collocated.
     */
    public boolean isCollocated() {
        return collocated;
    }

    @Override
    public byte getProtocolVersion() {
        return V3;
    }

    @Override
    protected void writeExternalData(ObjectOutput out) throws IOException {
        U.writeString(out, cacheName);
        U.writeString(out, qryTxt);
        out.writeBoolean(distributedJoins);
        out.writeBoolean(enforceJoinOrder);
        out.writeBoolean(loc);
        out.writeInt(pageSize);
        out.writeBoolean(lazy);
        out.writeBoolean(collocated);
    }

    @Override
    protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException {
        cacheName = U.readString(in);
        qryTxt = U.readString(in);
        distributedJoins = in.readBoolean();
        enforceJoinOrder = in.readBoolean();
        loc = in.readBoolean();
        pageSize = in.readInt();
        if (protoVer > V1)
            lazy = in.readBoolean();
        if (protoVer > V2)
            collocated = in.readBoolean();
    }

    @Override
    public String toString() {
        return S.toString(VisorQueryTaskArg.class, this);
    }
}


        U.writeString(out, cacheName);
        U.writeString(out, qryTxt);
        out.writeBoolean(distributedJoins);

package org.apache.ignite.internal.visor.query;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.VisorDataTransferObject;

/**
 * Arguments for {@link VisorQueryTask}.
 */
public class VisorQueryTaskArg extends VisorDataTransferObject {

    private static final long serialVersionUID = 0L;

    /**
     * Cache name for query.
     */
    private String cacheName;

    /**
     * Query text.
     */
    private String qryTxt;

    /**
     * Distributed joins enabled flag.
     */
    private boolean distributedJoins;

    /**
     * Enforce join order flag.
     */
    private boolean enforceJoinOrder;

    /**
     * Query contains only replicated tables flag.
     */
    private boolean replicatedOnly;

    /**
     * Flag whether to execute query locally.
     */
    private boolean loc;

    /**
     * Result batch size.
     */
    private int pageSize;

    /**
     * Lazy query execution flag
     */
    private boolean lazy;

    /**
     * Collocation flag.
     */
    private boolean collocated;

    public VisorQueryTaskArg() {
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, false, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, lazy, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     * @param collocated Collocation flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy, boolean collocated) {
        this.cacheName = cacheName;
        this.qryTxt = qryTxt;
        this.distributedJoins = distributedJoins;
        this.enforceJoinOrder = enforceJoinOrder;
        this.replicatedOnly = replicatedOnly;
        this.loc = loc;
        this.pageSize = pageSize;
        this.lazy = lazy;
        this.collocated = collocated;
    }

    public String getCacheName() {
        return cacheName;
    }

    public String getQueryText() {
        return qryTxt;
    }

    /**
     * @return Distributed joins enabled flag.
     */
    public boolean isDistributedJoins() {
        return distributedJoins;
    }

    /**
     * @return Enforce join order flag.
     */
    public boolean isEnforceJoinOrder() {
        return enforceJoinOrder;
    }

    /**
     * @return {@code true} If the query contains only replicated tables.
     */
    public boolean isReplicatedOnly() {
        return replicatedOnly;
    }

    /**
     * @return {@code true} If query should be executed locally.
     */
    public boolean isLocal() {
        return loc;
    }

    public int getPageSize() {
        return pageSize;
    }

    /**
     * Lazy query execution flag.
     *
     * @return Lazy flag.
     */
    public boolean getLazy() {
        return lazy;
    }

    /**
     * Flag indicating if this query is collocated.
     *
     * @return {@code true} If the query is collocated.
     */
    public boolean isCollocated() {
        return collocated;
    }

    @Override
    public byte getProtocolVersion() {
        return V3;
    }

    @Override
    protected void writeExternalData(ObjectOutput out) throws IOException {

        out.writeBoolean(enforceJoinOrder);
        out.writeBoolean(loc);
        out.writeInt(pageSize);
        out.writeBoolean(lazy);
        out.writeBoolean(collocated);
    }

    @Override
    protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException {
        cacheName = U.readString(in);
        qryTxt = U.readString(in);
        distributedJoins = in.readBoolean();
        enforceJoinOrder = in.readBoolean();
        loc = in.readBoolean();
        pageSize = in.readInt();
        if (protoVer > V1)
            lazy = in.readBoolean();
        if (protoVer > V2)
            collocated = in.readBoolean();
    }

    @Override
    public String toString() {
        return S.toString(VisorQueryTaskArg.class, this);
    }
}


out.writeBoolean(enforceJoinOrder);
        out.writeBoolean(loc);
        out.writeInt(pageSize);

package org.apache.ignite.internal.visor.query;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.VisorDataTransferObject;

/**
 * Arguments for {@link VisorQueryTask}.
 */
public class VisorQueryTaskArg extends VisorDataTransferObject {

    private static final long serialVersionUID = 0L;

    /**
     * Cache name for query.
     */
    private String cacheName;

    /**
     * Query text.
     */
    private String qryTxt;

    /**
     * Distributed joins enabled flag.
     */
    private boolean distributedJoins;

    /**
     * Enforce join order flag.
     */
    private boolean enforceJoinOrder;

    /**
     * Query contains only replicated tables flag.
     */
    private boolean replicatedOnly;

    /**
     * Flag whether to execute query locally.
     */
    private boolean loc;

    /**
     * Result batch size.
     */
    private int pageSize;

    /**
     * Lazy query execution flag
     */
    private boolean lazy;

    /**
     * Collocation flag.
     */
    private boolean collocated;

    public VisorQueryTaskArg() {
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, false, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy) {
        this(cacheName, qryTxt, distributedJoins, enforceJoinOrder, replicatedOnly, loc, pageSize, lazy, false);
    }

    /**
     * @param cacheName Cache name for query.
     * @param qryTxt Query text.
     * @param distributedJoins If {@code true} then distributed joins enabled.
     * @param enforceJoinOrder If {@code true} then enforce join order.
     * @param replicatedOnly {@code true} then query contains only replicated tables.
     * @param loc Flag whether to execute query locally.
     * @param pageSize Result batch size.
     * @param lazy Lazy query execution flag.
     * @param collocated Collocation flag.
     */
    public VisorQueryTaskArg(String cacheName, String qryTxt, boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean loc, int pageSize, boolean lazy, boolean collocated) {
        this.cacheName = cacheName;
        this.qryTxt = qryTxt;
        this.distributedJoins = distributedJoins;
        this.enforceJoinOrder = enforceJoinOrder;
        this.replicatedOnly = replicatedOnly;
        this.loc = loc;
        this.pageSize = pageSize;
        this.lazy = lazy;
        this.collocated = collocated;
    }

    public String getCacheName() {
        return cacheName;
    }

    public String getQueryText() {
        return qryTxt;
    }

    /**
     * @return Distributed joins enabled flag.
     */
    public boolean isDistributedJoins() {
        return distributedJoins;
    }

    /**
     * @return Enforce join order flag.
     */
    public boolean isEnforceJoinOrder() {
        return enforceJoinOrder;
    }

    /**
     * @return {@code true} If the query contains only replicated tables.
     */
    public boolean isReplicatedOnly() {
        return replicatedOnly;
    }

    /**
     * @return {@code true} If query should be executed locally.
     */
    public boolean isLocal() {
        return loc;
    }

    public int getPageSize() {
        return pageSize;
    }

    /**
     * Lazy query execution flag.
     *
     * @return Lazy flag.
     */
    public boolean getLazy() {
        return lazy;
    }

    /**
     * Flag indicating if this query is collocated.
     *
     * @return {@code true} If the query is collocated.
     */
    public boolean isCollocated() {
        return collocated;
    }

    @Override
    public byte getProtocolVersion() {
        return V3;
    }

    @Override
    protected void writeExternalData(ObjectOutput out) throws IOException {
        U.writeString(out, cacheName);
        U.writeString(out, qryTxt);
        out.writeBoolean(distributedJoins);

        out.writeBoolean(lazy);
        out.writeBoolean(collocated);
    }

    @Override
    protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException {
        cacheName = U.readString(in);
        qryTxt = U.readString(in);
        distributedJoins = in.readBoolean();
        enforceJoinOrder = in.readBoolean();
        loc = in.readBoolean();
        pageSize = in.readInt();
        if (protoVer > V1)
            lazy = in.readBoolean();
        if (protoVer > V2)
            collocated = in.readBoolean();
    }

    @Override
    public String toString() {
        return S.toString(VisorQueryTaskArg.class, this);
    }
}