/* * 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.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.transport.TIOStreamTransport; import org.apache.thrift.transport.TTransport; import org.springframework.beans.factory.InitializingBean; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.web.HttpRequestHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * <p/> * Simple {@link HttpRequestHandler} implementation that fields an incoming request and * forwards it to Thrift to handle. The {@link HttpRequestHandler} is the simplest of all * Spring Web components. * <p/> * <P>The easiest way to expose an HttpRequestHandler bean in Spring style * is to define it in Spring's root web application context and define * an {@link org.springframework.web.context.support.HttpRequestHandlerServlet} * in <code>web.xml</code>, pointing at the target HttpRequestHandler bean * through its <code>servlet-name</code> which needs to match the target bean name.* * <p/> * <P>This code is inspired by (and in large part duplicates) the {@link org.apache.thrift.server.TServlet}, but adapted * to work with Spring web infrastructure, benefiting from dependency injection and more. * <p/> * <P>The class provides the ability to register a (<EM>static</EM>) list of headers that * to be written on to the response. * <p/> * <P>Simple configuration using Java configuration might look like: </P> * <p/> * <CODE> * * @author Josh Long * @Bean public ThriftServiceExporter exporter (){ * MyThriftService.Iface serviceBean = new MyRegularServiceBean(); * ThriftServiceExporter e = new ThriftServiceExporter (); * e.setService(serviceBean); * e.setServiceInterface(MyThriftService.Iface.class); // or, MyThriftService.class * return e; * } * </CODE> * <p/> * <P>Thrift clients will by default * use the Thrift binary protocol unless the {@link #protocolFactory} is overridden. * @see org.apache.thrift.server.TServlet * @see HttpRequestHandler * @see org.springframework.remoting.caucho.HessianServiceExporter */ public class ThriftServiceExporter extends AbstractThriftExporter implements InitializingBean, HttpRequestHandler { private TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); private final Collection<Map.Entry<String, String>> customHeaders = new ArrayList<Map.Entry<String, String>>(); private MediaType mediaType; public void setProtocolFactory(TProtocolFactory inProtocolFactory) { this.protocolFactory = inProtocolFactory; } public void setMediaType(MediaType mediaType) { this.mediaType = mediaType; } public void addCustomHeader(String k, String v) { Map<String, String> header = new HashMap<String, String>(); header.put(k, v); this.customHeaders.add(header.entrySet().iterator().next()); } public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) { this.customHeaders.clear(); this.customHeaders.addAll(headers); } @Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); Assert.notNull(this.protocolFactory, "the 'protocolFactory' can't be null"); } @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { if (null != mediaType) { response.setContentType(mediaType.toString()); } if (null != this.customHeaders) { for (Map.Entry<String, String> header : this.customHeaders) { response.addHeader(header.getKey(), header.getValue()); } } InputStream in = request.getInputStream(); OutputStream out = response.getOutputStream(); // this is not pluggable from this end TTransport transport = new TIOStreamTransport(in, out); TProtocol protocol = protocolFactory.getProtocol(transport); processor.process(protocol, protocol); out.flush(); } catch (TException te) { throw new ServletException(te); } } }