Hessian client sends data to the server
Scenario: The project Log token processing, that is, when a user sends a request, generates a log token that is passed between the services and uses that token to record the log until the request is completed. All logs can be located based on the token.
Issue: Since the current project uses the Hessian protocol, all tokens must be passed using Hessian. Check the relevant data, found that can request the header to pass data.
Workaround: Define the thread-related request header context and increase the request header before the client sends the request. When the server obtains the request, it resolves the request header from the request and puts it into the context of the request header for use by the service side.
Realize:
1. Define the request header context Hessianheadercontext, with the following code:
package org.enyes.hessian
/ **
* Hessian protocol request header context.
* <pre>
* 1. This class uses ThreadLocal to pass the client request header information to the server.
* 2. Request header Before HessianProxy sends a request, attach the request header in this class to the request.
* 3. The server uses the HessianServiceExporter to obtain the request header and puts it in the HessianHeaderContext for the server to use.
* 4. Remember to call #close method after use to prevent ThreadLocal memory leak.
* </ pre>
* Created by enyes on 15/8/30.
* @see HessianProxy
* @see HessianProxyFactory
* @see HessianServiceExporter
* /
public class HessianHeaderContext {
private static final ThreadLocal <HessianHeaderContext> THREAD_LOCAL = new ThreadLocal <> ();
private Map <String, String> headers = new HashMap <> ();
private HessianHeaderContext () {
}
public static HessianHeaderContext getContext () {
HessianHeaderContext context = THREAD_LOCAL.get ();
if (context == null) {
context = new HessianHeaderContext ();
THREAD_LOCAL.set (context);
}
return context;
}
public void addHeader (String name, String value) {
headers.put (name, value);
}
public String getHeader (String name) {
return headers.get (name);
}
public Map <String, String> getHeaders () {
return headers;
}
public static void close () {
HessianHeaderContext context = THREAD_LOCAL.get ();
if (context! = null) {
context.headers.clear ();
THREAD_LOCAL.set (null);
}
}
}
2. Expand the client Hessianproxy, add the context request header when sending the request, as follows:
/ **
* Extend HessianProxy, add the request header in HessianHeaderContext to the request before the client sends the request.
* Created by enyes on 15/8/30.
* @see org.enyes.hessian.HessianProxyFactory
* /
public class HessianProxy extends com.caucho.hessian.client.HessianProxy {
protected HessianProxy (URL url, HessianProxyFactory factory) {
super (url, factory);
}
protected HessianProxy (URL url, HessianProxyFactory factory, Class <?> type) {
super (url, factory, type);
}
@Override
protected void addRequestHeaders (HessianConnection conn)
{
super.addRequestHeaders (conn);
// add Hessian Header
Map <String, String> headerMap = HessianHeaderContext.getContext (). GetHeaders ();
for (Map.Entry <String, String> entry: headerMap.entrySet ()) {
conn.addHeader (entry.getKey (), entry.getValue ());
}
}
}
3. Rewrite the hessianproxyfactory and integrate the newly expanded Hessianproxy as follows:
/ **
* Extend HessianProxyFactory to use the newly extended HessianProxy.
* <pre>
* Note: If using Spring integration, HessianProxyFactoryBean needs to set proxyFactory to this class of object.
* </ pre>
* Created by enyes on 15/8/30.
* @see HessianProxy
* /
public class HessianProxyFactory extends com.caucho.hessian.client.HessianProxyFactory {
@Override
public Object create (Class <?> api, URL url, ClassLoader loader)
{
if (api == null) {
throw new NullPointerException ("api must not be null for HessianProxyFactory.create ()");
}
InvocationHandler handler = new HessianProxy (url, this, api);
return Proxy.newProxyInstance (loader,
new Class [] {api, HessianRemoteObject.class},
handler);
}
}
Note: Hessianproxy and hessianproxyfactory are still named after the Hessian.jar, with no idea of the right name.
4. As the project is integrated with spring, the spring Hessianserviceexporter is expanded to parse the request header.
/ **
* Extend the Spring HessianServiceExporter class, when the server accepts the request,
* Receive the client request header and save it in the HessianHeaderContext for use by the server.
* Spring server xml file configures this class.
* Created by enyes on 15/8/30.
* /
public class HessianServiceExporter extends org.springframework.remoting.caucho.HessianServiceExporter {
@Override
public void handleRequest (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (! "POST" .equals (request.getMethod ())) {
throw new HttpRequestMethodNotSupportedException (request.getMethod (),
new String [] {"POST"}, "HessianServiceExporter only supports POST requests");
}
handleHessianHeader (request);
response.setContentType (CONTENT_TYPE_HESSIAN);
try {
invoke (request.getInputStream (), response.getOutputStream ());
} catch (Throwable ex) {
throw new NestedServletException ("Hessian skeleton invocation failed", ex);
} finally {
HessianHeaderContext.close ();
}
}
protected void handleHessianHeader (HttpServletRequest request) {
HessianHeaderContext context = HessianHeaderContext.getContext ();
Enumeration enumeration = request.getHeaderNames ();
while (enumeration.hasMoreElements ()) {
String name = enumeration.nextElement (). ToString ();
String value = request.getHeader (name);
context.addHeader (name, value);
}
}
}
5. Client Spring configuration file modification:
<bean id="hessianProxyFactory" class="org.enyes.hessian.HessianProxyFactory" />
<bean id="hessianServer" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="proxyFactory" ref="hessianProxyFactory" />
<property name="serviceUrl" value="${hessian.remote.provider_url}/hessianServer"/>
<property name="serviceInterface" value="org.enyes.hessian.service.HessianServer"/>
<property name="overloadEnabled" value="true" />
</bean>
6. Server-side spring configuration file, as follows:
<bean name="/hessianServer" class="org.enyes.hessian.HessianServiceExporter">
<property name="service" ref="hessianServerImpl"/>
<property name="serviceInterface" value="org.enyes.hessian.service.HessianServer"/>
</bean>
7. To the same time, after the client adds the request header to Hessianheadercontext, the server's hessianheadercontext can be obtained.
Controller test
@Controller() @RequestMapping("/hessian") public class HessianController { @Autowired private HessianServer hessianServer; @RequestMapping("passHeader") @ResponseBody public String passHeader() {
HessianHeaderContext context = HessianHeaderContext.getContext();
context.addHeader("log.token", "logToken_111111");
hessianServer.passHeader();
HessianHeaderContext.close(); return "success";
}
}
JUnit Test
@Test public void testPassHeader() throws MalformedURLException { //Spring Hessian代理Servlet String url = "http://localhost:8081/demoProvider/hessianServer";
HessianProxyFactory factory = new HessianProxyFactory();
HessianHeaderContext context = HessianHeaderContext.getContext();
context.addHeader("log.token", UUID.randomUUID().toString());
HessianServer api = (HessianServer) factory.create(HessianServer.class, url);
api.passHeader();
}
Service-Side services:
@Service
public class HessianServerImpl implements HessianServer {
private static final Logger LOG = LoggerFactory.getLogger(HessianServerImpl.class);
@Override
public String domainChange(User user) throws BusinessException {
throw new IllegalArgumentException( "domainChange. user:" + user);
}
@Override
public void passHeader() {
HessianHeaderContext context = HessianHeaderContext.getContext();
String logToken = context.getHeader("log.token");
LOG.warn("passHeader. Header[log.token]={}", logToken);
}
}
Conclusion: This article provides a way of thinking, readers can view the relevant source code or others blog.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Hessian client sends a request header to the server