Skip to content

Commit aef7881

Browse files
authored
Merge pull request #312 from cyb3r4nt/jsonrpc4j-issue-303
Remove usage of org.springframework.remoting.support.RemoteExporter #303
2 parents 755cccd + 42eef51 commit aef7881

9 files changed

+193
-37
lines changed

README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ JSON-RPC).
1616
* HTTP Server (`HttpServletRequest` \ `HttpServletResponse`)
1717
* Portlet Server (`ResourceRequest` \ `ResourceResponse`)
1818
* Socket Server (`StreamServer`)
19-
* Integration with the Spring Framework (`RemoteExporter`)
19+
* Integration with the Spring Framework
2020
* Streaming client
2121
* HTTP client
2222
* Dynamic client proxies
@@ -66,7 +66,7 @@ that take `InputStream`s and `OutputStream`s. Also in the library is a `JsonRpc
6666
which extends the `JsonRpcClient` to add HTTP support.
6767

6868
## Spring Framework
69-
jsonrpc4j provides a `RemoteExporter` to expose java services as JSON-RPC over HTTP without
69+
jsonrpc4j provides support for exposing java services as JSON-RPC over HTTP without
7070
requiring any additional work on the part of the programmer. The following example explains
7171
how to use the `JsonServiceExporter` within the Spring Framework.
7272

@@ -113,7 +113,9 @@ public class UserServiceImpl
113113
}
114114
```
115115

116-
Configure your service in spring as you would any other RemoteExporter:
116+
Configure your service in Spring as you would do it for any other Beans,
117+
and then add a reference of your service Bean into `JsonServiceExporter`
118+
by specifying the `service` and `serviceInterface` properties:
117119

118120
```xml
119121
<?xml version="1.0" encoding="UTF-8"?>

src/main/java/com/googlecode/jsonrpc4j/spring/AbstractCompositeJsonServiceExporter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public final void afterPropertiesSet()
9393
*
9494
* @throws Exception on error
9595
*/
96-
void exportService()
96+
protected void exportService()
9797
throws Exception {
9898
// no-op
9999
}

src/main/java/com/googlecode/jsonrpc4j/spring/AbstractJsonServiceExporter.java

+104-8
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import com.googlecode.jsonrpc4j.*;
5+
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
import org.springframework.aop.framework.ProxyFactory;
59
import org.springframework.beans.factory.BeanFactoryUtils;
610
import org.springframework.beans.factory.InitializingBean;
711
import org.springframework.context.ApplicationContext;
812
import org.springframework.context.ApplicationContextAware;
9-
import org.springframework.remoting.support.RemoteExporter;
13+
import org.springframework.util.ClassUtils;
1014

1115
import java.util.List;
1216
import java.util.concurrent.ExecutorService;
1317

1418
/**
15-
* {@link RemoteExporter} that exports services using Json
16-
* according to the JSON-RPC proposal specified at:
17-
* <a href="http://groups.google.com/group/json-rpc">
18-
* http://groups.google.com/group/json-rpc</a>.
19+
* Exports user defined services using JSON-RPC protocol
1920
*/
2021
@SuppressWarnings("unused")
21-
abstract class AbstractJsonServiceExporter extends RemoteExporter implements InitializingBean, ApplicationContextAware {
22+
abstract class AbstractJsonServiceExporter implements InitializingBean, ApplicationContextAware {
23+
private static final Logger logger = LoggerFactory.getLogger(AbstractJsonServiceExporter.class);
2224

2325
private ObjectMapper objectMapper;
2426
private JsonRpcServer jsonRpcServer;
@@ -36,6 +38,8 @@ abstract class AbstractJsonServiceExporter extends RemoteExporter implements Ini
3638
private List<JsonRpcInterceptor> interceptorList;
3739
private ExecutorService batchExecutorService = null;
3840
private long parallelBatchProcessingTimeout;
41+
private Object service;
42+
private Class<?> serviceInterface;
3943

4044
/**
4145
* {@inheritDoc}
@@ -49,7 +53,7 @@ public void afterPropertiesSet() throws Exception {
4953
try {
5054
objectMapper = BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, ObjectMapper.class);
5155
} catch (Exception e) {
52-
logger.debug(e);
56+
logger.debug("Failed to obtain objectMapper from application context", e);
5357
}
5458
}
5559
if (objectMapper == null) {
@@ -93,7 +97,7 @@ public void afterPropertiesSet() throws Exception {
9397
*
9498
* @throws Exception on error
9599
*/
96-
void exportService()
100+
protected void exportService()
97101
throws Exception {
98102
// no-op
99103
}
@@ -214,4 +218,96 @@ public void setBatchExecutorService(ExecutorService batchExecutorService) {
214218
public void setParallelBatchProcessingTimeout(long parallelBatchProcessingTimeout) {
215219
this.parallelBatchProcessingTimeout = parallelBatchProcessingTimeout;
216220
}
221+
222+
/**
223+
* Set the service to export.
224+
* Typically populated via a bean reference.
225+
*/
226+
public void setService(Object service) {
227+
this.service = service;
228+
}
229+
230+
/**
231+
* Return the service to export.
232+
*/
233+
public Object getService() {
234+
return this.service;
235+
}
236+
237+
/**
238+
* Set the interface of the service to export.
239+
* The interface must be suitable for the particular service and remoting strategy.
240+
*/
241+
public void setServiceInterface(Class<?> serviceInterface) {
242+
if (serviceInterface == null) {
243+
throw new IllegalArgumentException("'serviceInterface' must not be null");
244+
}
245+
if (!serviceInterface.isInterface()) {
246+
throw new IllegalArgumentException("'serviceInterface' must be an interface");
247+
}
248+
this.serviceInterface = serviceInterface;
249+
}
250+
251+
/**
252+
* Return the interface of the service to export.
253+
*/
254+
public Class<?> getServiceInterface() {
255+
return this.serviceInterface;
256+
}
257+
258+
259+
/**
260+
* Check whether a service reference has been set,
261+
* and whether it matches the specified service.
262+
* @see #setServiceInterface
263+
* @see #setService
264+
*/
265+
protected void checkServiceInterface() throws IllegalArgumentException {
266+
Class<?> serviceInterface = getServiceInterface();
267+
if (serviceInterface == null) {
268+
throw new IllegalArgumentException("Property 'serviceInterface' is required");
269+
}
270+
271+
Object service = getService();
272+
if (service instanceof String) {
273+
throw new IllegalArgumentException(
274+
"Service [" + service + "] is a String rather than an actual service reference:"
275+
+ " Have you accidentally specified the service bean name as value "
276+
+ " instead of as reference?"
277+
);
278+
}
279+
if (!serviceInterface.isInstance(service)) {
280+
throw new IllegalArgumentException(
281+
"Service interface [" + serviceInterface.getName()
282+
+ "] needs to be implemented by service [" + service + "] of class ["
283+
+ service.getClass().getName() + "]"
284+
);
285+
}
286+
}
287+
288+
289+
/**
290+
* Get a proxy for the given service object, implementing the specified
291+
* service interface.
292+
* <p>Used to export a proxy that does not expose any internals but just
293+
* a specific interface intended for remote access.
294+
*
295+
* @return the proxy
296+
* @see #setServiceInterface
297+
* @see #setService
298+
*/
299+
protected Object getProxyForService() {
300+
Object targetService = getService();
301+
if (targetService == null) {
302+
throw new IllegalArgumentException("Property 'service' is required");
303+
}
304+
checkServiceInterface();
305+
306+
ProxyFactory proxyFactory = new ProxyFactory();
307+
proxyFactory.addInterface(getServiceInterface());
308+
proxyFactory.setTarget(targetService);
309+
proxyFactory.setOpaque(true);
310+
311+
return proxyFactory.getProxy(ClassUtils.getDefaultClassLoader());
312+
}
217313
}

src/main/java/com/googlecode/jsonrpc4j/spring/AutoJsonRpcServiceExporter.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public class AutoJsonRpcServiceExporter implements BeanFactoryPostProcessor {
4444

4545
private ObjectMapper objectMapper;
4646
private ErrorResolver errorResolver = null;
47-
private Boolean registerTraceInterceptor;
4847
private boolean backwardsCompatible = true;
4948
private boolean rethrowExceptions = false;
5049
private boolean allowExtraParams = false;
@@ -141,10 +140,6 @@ private void registerServiceProxy(DefaultListableBeanFactory defaultListableBean
141140
builder.addPropertyValue("invocationListener", invocationListener);
142141
}
143142

144-
if (registerTraceInterceptor != null) {
145-
builder.addPropertyValue("registerTraceInterceptor", registerTraceInterceptor);
146-
}
147-
148143
if (httpStatusCodeProvider != null) {
149144
builder.addPropertyValue("httpStatusCodeProvider", httpStatusCodeProvider);
150145
}
@@ -225,12 +220,16 @@ public void setAllowLessParams(boolean allowLessParams) {
225220
}
226221

227222
/**
228-
* See {@link org.springframework.remoting.support.RemoteExporter#setRegisterTraceInterceptor(boolean)}
223+
* See {@code org.springframework.remoting.support.RemoteExporter#setRegisterTraceInterceptor(boolean)}
224+
* <p>
225+
* Note: this method is deprecated and marked for removal.
226+
* {@code RemoteExporter} and {@code TraceInterceptor-s} are no longer supported.
229227
*
230228
* @param registerTraceInterceptor the registerTraceInterceptor value to set
231229
*/
230+
@Deprecated
232231
public void setRegisterTraceInterceptor(boolean registerTraceInterceptor) {
233-
this.registerTraceInterceptor = registerTraceInterceptor;
232+
// NOOP
234233
}
235234

236235
/**

src/main/java/com/googlecode/jsonrpc4j/spring/AutoJsonRpcServiceImplExporter.java

+8-9
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public class AutoJsonRpcServiceImplExporter implements BeanFactoryPostProcessor
5050

5151
private ObjectMapper objectMapper;
5252
private ErrorResolver errorResolver = null;
53-
private Boolean registerTraceInterceptor;
5453
private boolean backwardsCompatible = true;
5554
private boolean rethrowExceptions = false;
5655
private boolean allowExtraParams = false;
@@ -181,11 +180,7 @@ private void registerServiceProxy(DefaultListableBeanFactory defaultListableBean
181180
if (invocationListener != null) {
182181
builder.addPropertyValue("invocationListener", invocationListener);
183182
}
184-
185-
if (registerTraceInterceptor != null) {
186-
builder.addPropertyValue("registerTraceInterceptor", registerTraceInterceptor);
187-
}
188-
183+
189184
if (httpStatusCodeProvider != null) {
190185
builder.addPropertyValue("httpStatusCodeProvider", httpStatusCodeProvider);
191186
}
@@ -280,14 +275,18 @@ public void setAllowExtraParams(boolean allowExtraParams) {
280275
public void setAllowLessParams(boolean allowLessParams) {
281276
this.allowLessParams = allowLessParams;
282277
}
283-
278+
284279
/**
285-
* See {@link org.springframework.remoting.support.RemoteExporter#setRegisterTraceInterceptor(boolean)}
280+
* See {@code org.springframework.remoting.support.RemoteExporter#setRegisterTraceInterceptor(boolean)}
281+
* <p>
282+
* Note: this method is deprecated and marked for removal.
283+
* {@code RemoteExporter} and {@code TraceInterceptor-s} are no longer supported.
286284
*
287285
* @param registerTraceInterceptor the registerTraceInterceptor value to set
288286
*/
287+
@Deprecated
289288
public void setRegisterTraceInterceptor(boolean registerTraceInterceptor) {
290-
this.registerTraceInterceptor = registerTraceInterceptor;
289+
// NOOP
291290
}
292291

293292
/**

src/main/java/com/googlecode/jsonrpc4j/spring/JsonServiceExporter.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
import java.io.IOException;
1010

1111
/**
12-
* {@link HttpRequestHandler} that exports services using Json
13-
* according to the JSON-RPC proposal specified at:
14-
* <a href="http://groups.google.com/group/json-rpc">
15-
* http://groups.google.com/group/json-rpc</a>.
12+
* {@link HttpRequestHandler} that exports user services using JSON-RPC over HTTP protocol
1613
*/
1714
public class JsonServiceExporter extends AbstractJsonServiceExporter implements HttpRequestHandler {
1815

src/main/java/com/googlecode/jsonrpc4j/spring/JsonStreamServiceExporter.java

+1-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@
1111

1212

1313
/**
14-
* {@link org.springframework.remoting.support.RemoteExporter RemoteExporter}
15-
* that exports services using Json according to the JSON-RPC proposal specified
16-
* at:
17-
* <a href="http://groups.google.com/group/json-rpc">
18-
* http://groups.google.com/group/json-rpc</a>.
14+
* Exports user defined services as streaming server, which provides JSON-RPC over sockets.
1915
*/
2016
@SuppressWarnings("unused")
2117
public class JsonStreamServiceExporter extends AbstractJsonServiceExporter implements DisposableBean {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.googlecode.jsonrpc4j.spring;
2+
3+
import org.junit.Test;
4+
import org.junit.runner.RunWith;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.context.ApplicationContext;
7+
import org.springframework.test.context.ContextConfiguration;
8+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
9+
10+
import com.googlecode.jsonrpc4j.spring.service.Service;
11+
import com.googlecode.jsonrpc4j.spring.service.ServiceImpl;
12+
13+
import static org.junit.Assert.*;
14+
15+
/**
16+
* This test ensures that {@link com.googlecode.jsonrpc4j.spring.JsonServiceExporter} bean is
17+
* constructed according to Spring Framework configuration.
18+
*/
19+
@RunWith(SpringJUnit4ClassRunner.class)
20+
@ContextConfiguration("classpath:serverApplicationContextC.xml")
21+
public class JsonServiceExporterIntegrationTest {
22+
23+
private static final String BEAN_NAME_AND_URL_PATH = "/UserService.json";
24+
25+
@Autowired
26+
private ApplicationContext applicationContext;
27+
28+
@Test
29+
public void testExportedService() {
30+
assertNotNull(applicationContext);
31+
32+
// check that the bean was only exported on the configured path.
33+
{
34+
Object bean = applicationContext.getBean(BEAN_NAME_AND_URL_PATH);
35+
assertEquals(JsonServiceExporter.class, bean.getClass());
36+
37+
String[] names = applicationContext.getBeanNamesForType(JsonServiceExporter.class);
38+
assertNotNull(names);
39+
assertEquals(1, names.length);
40+
assertEquals(BEAN_NAME_AND_URL_PATH, names[0]);
41+
}
42+
43+
// check that service classes were also successfully configured in the context.
44+
45+
{
46+
Service service = applicationContext.getBean(Service.class);
47+
assertTrue(service instanceof ServiceImpl);
48+
49+
ServiceImpl serviceImpl = applicationContext.getBean(ServiceImpl.class);
50+
assertNotNull(serviceImpl);
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xmlns="http://www.springframework.org/schema/beans"
3+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
4+
5+
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
6+
7+
<bean id="userService" class="com.googlecode.jsonrpc4j.spring.service.ServiceImpl">
8+
</bean>
9+
10+
<bean name="/UserService.json" class="com.googlecode.jsonrpc4j.spring.JsonServiceExporter">
11+
<property name="service" ref="userService"/>
12+
<property name="serviceInterface" value="com.googlecode.jsonrpc4j.spring.service.Service"/>
13+
</bean>
14+
</beans>

0 commit comments

Comments
 (0)