Package com.facebook.swift.service.guice

Source Code of com.facebook.swift.service.guice.ThriftClientBinder

/*
* Copyright (C) 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.swift.service.guice;

import com.facebook.swift.service.ThriftClient;
import com.facebook.swift.service.ThriftClientConfig;
import com.facebook.swift.service.ThriftClientManager;
import com.facebook.swift.service.ThriftClientManager.ThriftClientMetadata;
import com.google.common.base.Preconditions;
import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeToken;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import org.weakref.jmx.guice.ExportBinder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.UUID;

import static com.facebook.swift.service.ThriftClientManager.DEFAULT_NAME;
import static com.facebook.swift.service.metadata.ThriftServiceMetadata.getThriftServiceAnnotation;
import static io.airlift.configuration.ConfigurationModule.bindConfig;
import static java.lang.String.format;

public class ThriftClientBinder
{
    public static ThriftClientBinder thriftClientBinder(Binder binder)
    {
        return new ThriftClientBinder(binder);
    }

    private final Binder binder;

    private ThriftClientBinder(Binder binder)
    {
        this.binder = binder;
    }

    public <T> void bindThriftClient(Class<T> clientInterface)
    {
        Preconditions.checkNotNull(clientInterface, "clientInterface is null");
        String typeName = getServiceName(clientInterface);

        // Bind ThriftClientConfig with random @Named annotation
        // We generate a random Named annotation for binding the ThriftClientConfig because we
        // need one of these for each clientType+annotationType pair.  Without this, the
        // ThriftClientConfig bindings collapse to a single instance which is shared by all
        // clients.
        Named thriftClientConfigKey = Names.named(UUID.randomUUID().toString());
        bindConfig(binder).annotatedWith(thriftClientConfigKey).prefixedWith(typeName).to(ThriftClientConfig.class);

        // Bind ThriftClient to a provider which knows how to find the ThriftClientConfig using
        // the random @Named annotation
        TypeLiteral<ThriftClient<T>> typeLiteral = toThriftClientTypeLiteral(clientInterface);
        ThriftClientProvider<T> provider = new ThriftClientProvider<>(clientInterface, DEFAULT_NAME, Key.get(ThriftClientConfig.class, thriftClientConfigKey));
        binder.bind(typeLiteral).toProvider(provider).in(Scopes.SINGLETON);

        // Export client to jmx
        ExportBinder.newExporter(binder)
                .export(Key.get(typeLiteral))
                .as(format("com.facebook.swift.client:type=%s,clientName=%s",
                        typeName,
                        DEFAULT_NAME));

        // Add the provider itself to a SetBinding so later we can export the thrift methods to JMX
        Multibinder.newSetBinder(binder, ThriftClientProvider.class).addBinding().toInstance(provider);
    }

    public <T> void bindThriftClient(Class<T> clientInterface, Class<? extends Annotation> annotationType)
    {
        Preconditions.checkNotNull(clientInterface, "clientInterface is null");
        String typeName = getServiceName(clientInterface);
        String name = annotationType.getSimpleName();

        // Bind ThriftClientConfig with random @Named annotation
        // see comment on random Named annotation above
        Named thriftClientConfigKey = Names.named(UUID.randomUUID().toString());
        String prefix = String.format("%s.%s", typeName, name);
        bindConfig(binder).annotatedWith(thriftClientConfigKey).prefixedWith(prefix).to(ThriftClientConfig.class);

        // Bind ThriftClient to a provider which knows how to find the ThriftClientConfig using
        // the random @Named annotation
        TypeLiteral<ThriftClient<T>> typeLiteral = toThriftClientTypeLiteral(clientInterface);
        ThriftClientProvider<T> provider = new ThriftClientProvider<>(clientInterface,
                name,
                Key.get(ThriftClientConfig.class, thriftClientConfigKey));
        binder.bind(Key.get(typeLiteral, annotationType)).toProvider(provider).in(Scopes.SINGLETON);

        // Export client to jmx
        ExportBinder.newExporter(binder)
                .export(Key.get(typeLiteral))
                .as(format("com.facebook.swift.client:type=%s,clientName=%s",
                           typeName,
                           name));

        // Add the provider itself to a SetBinding so later we can export the thrift methods to JMX
        Multibinder.newSetBinder(binder, ThriftClientProvider.class).addBinding().toInstance(provider);
    }

    private static String getServiceName(Class<?> clientInterface)
    {
        String serviceName = getThriftServiceAnnotation(clientInterface).value();
        if (!serviceName.isEmpty()) {
            return serviceName;
        }
        return clientInterface.getSimpleName();
    }

    /**
     * @return TypeLiteral<ThriftClient<T>>
     */
    private static <T> TypeLiteral<ThriftClient<T>> toThriftClientTypeLiteral(Class<T> clientInterface)
    {
        // build a TypeLiteral<ThriftClientProvider<T>> where T is bound to the actual clientInterface class
        @SuppressWarnings("serial")
        Type javaType = new TypeToken<ThriftClient<T>>() {}
                .where(new TypeParameter<T>() {}, TypeToken.of(clientInterface))
                .getType();
        return (TypeLiteral<ThriftClient<T>>) TypeLiteral.get(javaType);
    }

    public static class ThriftClientProvider<T> implements Provider<ThriftClient<T>>
    {
        private final Class<T> clientType;
        private final String clientName;
        private final Key<ThriftClientConfig> configKey;
        private ThriftClientManager clientManager;
        private Injector injector;

        public ThriftClientProvider(Class<T> clientType, String clientName, Key<ThriftClientConfig> configKey)
        {
            Preconditions.checkNotNull(clientType, "clientInterface is null");
            Preconditions.checkNotNull(clientName, "clientName is null");
            Preconditions.checkNotNull(configKey, "configKey is null");
            this.clientType = clientType;
            this.clientName = clientName;
            this.configKey = configKey;
        }

        @Inject
        public void setClientManager(ThriftClientManager clientManager)
        {
            this.clientManager = clientManager;
        }

        @Inject
        public void setInjector(Injector injector)
        {
            this.injector = injector;
        }

        @Override
        public ThriftClient<T> get()
        {
            Preconditions.checkState(clientManager != null, "clientManager has not been set");
            Preconditions.checkState(injector != null, "injector has not been set");
            ThriftClientConfig clientConfig = injector.getInstance(configKey);
            return new ThriftClient<>(clientManager, clientType, clientConfig, clientName);
        }

        public ThriftClientMetadata getClientMetadata()
        {
            Preconditions.checkState(clientManager != null, "clientManager has not been set");
            return clientManager.getClientMetadata(clientType, clientName);
        }

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

            ThriftClientProvider<?> that = (ThriftClientProvider<?>) o;

            if (!clientName.equals(that.clientName)) {
                return false;
            }
            if (!clientType.equals(that.clientType)) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode()
        {
            int result = clientType.hashCode();
            result = 31 * result + clientName.hashCode();
            return result;
        }
    }
}
TOP

Related Classes of com.facebook.swift.service.guice.ThriftClientBinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.