All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.swift.service.guice.ThriftClientBinder Maven / Gradle / Ivy

/*
 * 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.ThriftClientEventHandler;
import com.facebook.swift.service.ThriftClientManager;
import com.facebook.swift.service.ThriftClientManager.ThriftClientMetadata;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
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.Set;
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.ConfigBinder.configBinder;
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  ClientEventHandlersBinder bindThriftClient(Class 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(typeName + "-" + UUID.randomUUID().toString());
        configBinder(binder).bindConfig(ThriftClientConfig.class, thriftClientConfigKey, typeName);

        // Bind ThriftClient to a provider which knows how to find the ThriftClientConfig using
        // the random @Named annotation
        Multibinder eventHandlersBinder = Multibinder.newSetBinder(binder,
                ThriftClientEventHandler.class, thriftClientConfigKey);
        ThriftClientProvider provider = new ThriftClientProvider<>(clientInterface, DEFAULT_NAME,
                Key.get(ThriftClientConfig.class, thriftClientConfigKey),
                Key.get(new TypeLiteral>() {}, thriftClientConfigKey));
        TypeLiteral> typeLiteral = toThriftClientTypeLiteral(clientInterface);
        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);

        return new ClientEventHandlersBinder(eventHandlersBinder);
    }

    public  ClientEventHandlersBinder bindThriftClient(Class clientInterface, Class 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(typeName + "-" + UUID.randomUUID().toString());
        String prefix = String.format("%s.%s", typeName, name);
        configBinder(binder).bindConfig(ThriftClientConfig.class, thriftClientConfigKey, prefix);

        // Bind ThriftClient to a provider which knows how to find the ThriftClientConfig using
        // the random @Named annotation
        Multibinder eventHandlersBinder = Multibinder.newSetBinder(binder,
                ThriftClientEventHandler.class, thriftClientConfigKey);
        ThriftClientProvider provider = new ThriftClientProvider<>(clientInterface,
                name,
                Key.get(ThriftClientConfig.class, thriftClientConfigKey),
                Key.get(new TypeLiteral>() {}, thriftClientConfigKey));
        TypeLiteral> typeLiteral = toThriftClientTypeLiteral(clientInterface);
        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);

        return new ClientEventHandlersBinder(eventHandlersBinder);
    }

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

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

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

        public ThriftClientProvider(Class clientType, String clientName, Key configKey,
                                    Key> eventHandlersKey)
        {
            Preconditions.checkNotNull(clientType, "clientInterface is null");
            Preconditions.checkNotNull(clientName, "clientName is null");
            Preconditions.checkNotNull(configKey, "configKey is null");
            Preconditions.checkNotNull(eventHandlersKey, "eventHandlersKey is null");
            this.eventHandlersKey = eventHandlersKey;
            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 get()
        {
            Preconditions.checkState(clientManager != null, "clientManager has not been set");
            Preconditions.checkState(injector != null, "injector has not been set");
            ThriftClientConfig clientConfig = injector.getInstance(configKey);
            Set handlersSet = injector.getInstance(eventHandlersKey);
            return new ThriftClient<>(clientManager, clientType, clientConfig, clientName, ImmutableList.copyOf(handlersSet));
        }

        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;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy