/* * Copyright 2011 the original author or authors. * * 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 org.springframework.remoting.thrift; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.obm.thrift.util.ThriftUtil; import org.springframework.remoting.support.RemoteAccessor; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * Builds proxies that can connect to a Thrift RPC service. * <p/> * <P>Thrift clients will by default * use the Thrift binary protocol unless the {@link #protocolFactory} is overridden. * * @author Josh Long * @see org.springframework.remoting.caucho.HessianProxyFactoryBean */ public class ThriftProxyFactoryBean<T> extends RemoteAccessor implements InitializingBean, MethodInterceptor, FactoryBean<T> { // default protocol will be binary private TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); // transport is optional private TTransport transport; // protocol is optional private TProtocol protocol; // the client as created using the Thrift APIs private Object client; // the proxy that we create which is in turn returned to the client of this class private Object serviceProxy; private String host = "127.0.0.1"; private int port = ThriftUtil.DEFAULT_PORT; public void setProtocolFactory(TProtocolFactory inProtocolFactory) { this.protocolFactory = inProtocolFactory; } public void setTransport(TTransport transport) { this.transport = transport; } public void setProtocol(TProtocol protocol) { this.protocol = protocol; } public void setPort(int port) { this.port = port; } public void setHost(String host) { this.host = host; } @Override public void setServiceInterface(Class serviceInterface) { super.setServiceInterface(ThriftUtil.buildServiceInterface(serviceInterface)); } public T getObject() { return (T) this.serviceProxy; } public Class<?> getObjectType() { return getServiceInterface(); } public boolean isSingleton() { return true; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { // now, unless i miss my mark, the interface of this proxy and the proxy weve created locally are the same, // so it should be a simple matter to forward the MethodInvocation on to the local client Method method = invocation.getMethod(); if (logger.isDebugEnabled()) { logger.debug("invoking " + invocation.toString() + " on the client proxy"); } return method.invoke(this.client, invocation.getArguments()); } @Override public void afterPropertiesSet() { if (getServiceInterface() == null) { throw new IllegalArgumentException("Property 'serviceInterface' is required"); } if (this.transport == null) { Assert.notNull(this.host, "Property 'host' is required"); Assert.isTrue(this.port > 0, "Property 'port' is required and must be greater than 0"); this.transport = new TSocket(this.host, this.port); } if (this.protocol == null) { this.protocol = this.protocolFactory.getProtocol(this.transport); } try { Class thriftClass = getServiceInterface().getEnclosingClass(); Assert.notNull(thriftClass, "the enclosing class must not be null"); Class clientClass = ThriftUtil.getThriftServiceInnerClassOrNull(thriftClass, "$Client", false); Assert.notNull(clientClass, "the client class must not be null "); Constructor constructor = ClassUtils.getConstructorIfAvailable(clientClass, TProtocol.class); this.client = constructor.newInstance(this.protocol); Assert.notNull(this.client, "the Thrift RPC client was not correctly created. Aborting."); this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader()); this.transport.open(); } catch (Exception e) { throw new RuntimeException(e); } } }