├── settings.gradle ├── .travis.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── RELEASE-NOTES.md ├── src ├── test │ ├── java │ │ └── com │ │ │ └── googlecode │ │ │ └── jsonrpc4j │ │ │ ├── spring │ │ │ ├── service │ │ │ │ ├── ServiceImpl.java │ │ │ │ └── Service.java │ │ │ ├── serviceb │ │ │ │ ├── package.java │ │ │ │ ├── Temperature.java │ │ │ │ ├── NoopTemperatureImpl.java │ │ │ │ └── TemperatureImpl.java │ │ │ ├── servicesansinterface │ │ │ │ └── ServiceSansInterfaceImpl.java │ │ │ ├── JsonRpcPathClientIntegrationTest.java │ │ │ ├── JsonRpcPathServerIntegrationTest.java │ │ │ ├── JsonRpcPathClientIntegrationTestB.java │ │ │ ├── JsonServiceExporterIntegrationTest.java │ │ │ └── JsonRpcPathServerIntegrationTestB.java │ │ │ ├── util │ │ │ ├── FakeTimingOutService.java │ │ │ ├── TestThrowable.java │ │ │ ├── CustomTestException.java │ │ │ ├── FakeTimingOutServiceImpl.java │ │ │ ├── FakeServiceInterface.java │ │ │ ├── FakeServiceInterfaceImpl.java │ │ │ ├── LocalThreadServer.java │ │ │ ├── BaseRestTest.java │ │ │ ├── JettyServer.java │ │ │ └── Util.java │ │ │ ├── server │ │ │ ├── JsonRpcServerSequentialBatchProcessingTest.java │ │ │ ├── JsonRpcServerParallelBatchProcessingTest.java │ │ │ ├── JsonRpcServerAnnotateMethodTest.java │ │ │ ├── MultiServiceTest.java │ │ │ ├── DefaultHttpStatusCodeProviderTest.java │ │ │ ├── HttpStatusCodeProviderTest.java │ │ │ ├── JsonRpcErrorsTest.java │ │ │ └── JsonRpcServerBatchTest.java │ │ │ ├── integration │ │ │ ├── TimeoutTest.java │ │ │ ├── SimpleTest.java │ │ │ ├── HttpClientTest.java │ │ │ ├── HttpCodeTest.java │ │ │ ├── ServerClientTest.java │ │ │ └── StreamServerTest.java │ │ │ └── client │ │ │ └── JsonRpcClientTest.java │ └── resources │ │ ├── clientApplicationContextB.xml │ │ ├── log4j2.xml │ │ ├── serverApplicationContext.xml │ │ ├── clientApplicationContext.xml │ │ ├── serverApplicationContextB.xml │ │ └── serverApplicationContextC.xml └── main │ └── java │ └── com │ └── googlecode │ └── jsonrpc4j │ ├── spring │ ├── JsonRpcReference.java │ ├── JsonServiceExporter.java │ ├── CompositeJsonServiceExporter.java │ ├── AutoJsonRpcServiceImpl.java │ ├── rest │ │ ├── SslClientHttpRequestFactory.java │ │ ├── JsonRpcResponseErrorHandler.java │ │ ├── JsonRestProxyFactoryBean.java │ │ └── MappingJacksonRPC2HttpMessageConverter.java │ ├── CompositeJsonStreamServiceExporter.java │ ├── JsonStreamServiceExporter.java │ ├── AutoJsonRpcClientProxyFactory.java │ ├── AutoJsonRpcClientProxyCreator.java │ ├── JsonProxyFactoryBean.java │ └── AbstractCompositeJsonServiceExporter.java │ ├── JsonRpcParamsPassMode.java │ ├── HttpException.java │ ├── JsonRpcFixedParams.java │ ├── RequestIDGenerator.java │ ├── ExceptionResolver.java │ ├── JsonRpcParam.java │ ├── JsonRpcFixedParam.java │ ├── JsonRpcErrors.java │ ├── JsonRpcService.java │ ├── VarArgsUtil.java │ ├── Util.java │ ├── JsonRpcCallback.java │ ├── RequestInterceptor.java │ ├── JsonRpcMethod.java │ ├── DefaultErrorResolver.java │ ├── StreamEndedException.java │ ├── JsonRpcClientException.java │ ├── JsonRpcError.java │ ├── ErrorData.java │ ├── ConvertedParameterTransformer.java │ ├── JsonResponse.java │ ├── HttpStatusCodeProvider.java │ ├── InvocationListener.java │ ├── NoCloseOutputStream.java │ ├── MultipleExceptionResolver.java │ ├── MultipleErrorResolver.java │ ├── JsonUtil.java │ ├── ReadContext.java │ ├── NoCloseInputStream.java │ ├── AnnotationsErrorResolver.java │ ├── MultipleInvocationListener.java │ ├── IJsonRpcClient.java │ ├── JsonRpcInterceptor.java │ ├── DefaultHttpStatusCodeProvider.java │ ├── ErrorResolver.java │ ├── JsonRpcMultiServer.java │ └── DefaultExceptionResolver.java ├── .github ├── dependabot.yml └── workflows │ ├── gradle-wrapper-validation.yml │ └── gradle-test-validation.yml ├── .gitignore ├── LICENSE ├── pom.gradle ├── gradlew.bat └── publishing.gradle /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jsonrpc4j' 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk17 4 | install: true 5 | script: 6 | - gradle build -i 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briandilley/jsonrpc4j/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /RELEASE-NOTES.md: -------------------------------------------------------------------------------- 1 | For a summary of releases and the changes in each release, please see: 2 | 3 | https://github.com/briandilley/jsonrpc4j/releases -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/service/ServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring.service; 2 | 3 | public class ServiceImpl implements Service { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/util/FakeTimingOutService.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.util; 2 | 3 | public interface FakeTimingOutService { 4 | void doTimeout(); 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/service/Service.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring.service; 2 | 3 | import com.googlecode.jsonrpc4j.JsonRpcService; 4 | 5 | @JsonRpcService("TestService") 6 | public interface Service { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: gradle 8 | directory: "/" 9 | schedule: 10 | interval: weekly 11 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/util/TestThrowable.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.util; 2 | 3 | @SuppressWarnings({"serial", "WeakerAccess"}) 4 | public class TestThrowable extends Throwable { 5 | public TestThrowable(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | validation: 6 | name: "Gradle wrapper validation" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v5 10 | - uses: gradle/wrapper-validation-action@v3 11 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/spring/JsonRpcReference.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target({ElementType.FIELD}) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Inherited 8 | public @interface JsonRpcReference { 9 | 10 | String address() default ""; 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/serviceb/package.java: -------------------------------------------------------------------------------- 1 | /** 2 | *
This set of service classes is designed to test the 3 | * {@link com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImplExporter} bean used to 4 | * help with exposing JSON-RPC services. 5 | *
6 | */ 7 | 8 | package com.googlecode.jsonrpc4j.spring.serviceb; 9 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/JsonRpcParamsPassMode.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j; 2 | 3 | /** 4 | * The JSON-RPC specification allows either passing parameters as an Array, for by-position arguments, or as an Object, 5 | * for by-name arguments. 6 | * 7 | */ 8 | public enum JsonRpcParamsPassMode { 9 | AUTO, 10 | ARRAY, 11 | OBJECT 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/util/CustomTestException.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.util; 2 | 3 | public class CustomTestException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public CustomTestException() { 8 | } 9 | 10 | public CustomTestException(String msg) { 11 | super(msg); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/serviceb/Temperature.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring.serviceb; 2 | 3 | import com.googlecode.jsonrpc4j.JsonRpcService; 4 | 5 | @JsonRpcService( 6 | "api/temperature" // note the absence of a leading slash 7 | ) 8 | public interface Temperature { 9 | 10 | @SuppressWarnings("unused") 11 | Integer currentTemperature(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/resources/clientApplicationContextB.xml: -------------------------------------------------------------------------------- 1 |This implementation should not be picked up by the 5 | * {@link com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImplExporter} 6 | * bean because it does not have the necessary annotation.
7 | */ 8 | 9 | public class NoopTemperatureImpl implements Temperature { 10 | 11 | @Override 12 | public Integer currentTemperature() { 13 | return 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 |This implementation should be picked up by the 7 | * {@link com.googlecode.jsonrpc4j.spring.AutoJsonRpcServiceImplExporter} 8 | * class.
9 | */ 10 | 11 | @AutoJsonRpcServiceImpl(additionalPaths = { 12 | "/api-web/temperature" // note the leading slash 13 | }) 14 | public class TemperatureImpl implements Temperature { 15 | 16 | @Override 17 | public Integer currentTemperature() { 18 | return 25; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/server/JsonRpcServerSequentialBatchProcessingTest.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.server; 2 | 3 | import com.googlecode.jsonrpc4j.JsonRpcServer; 4 | import com.googlecode.jsonrpc4j.util.Util; 5 | import org.easymock.EasyMockRunner; 6 | import org.junit.Before; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(EasyMockRunner.class) 10 | public class JsonRpcServerSequentialBatchProcessingTest extends JsonRpcServerBatchTest { 11 | 12 | @Before 13 | public void setup() { 14 | jsonRpcServer = new JsonRpcServer(Util.mapper, mockService, JsonRpcServerTest.ServiceInterface.class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/servicesansinterface/ServiceSansInterfaceImpl.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring.servicesansinterface; 2 | 3 | import com.googlecode.jsonrpc4j.JsonRpcService; 4 | 5 | /** 6 | *Unlike the {@link com.googlecode.jsonrpc4j.spring.service.Service} / 7 | * {@link com.googlecode.jsonrpc4j.spring.service.ServiceImpl} example, this case has no interface 8 | * so the bean has the @JsonRpcService directly into the implementation. This setup worked 9 | * in jsonrpc4j 1.1, but failed in 1.2.
10 | */ 11 | 12 | @JsonRpcService("ServiceSansInterface") 13 | public class ServiceSansInterfaceImpl { 14 | } 15 | -------------------------------------------------------------------------------- /src/test/resources/clientApplicationContext.xml: -------------------------------------------------------------------------------- 1 |8 | * Any exceptions thrown in the {@link ConvertedParameterTransformer#transformConvertedParameters(Object, Object[])} 9 | * method, will be returned as an error to the JSON-RPC client. 10 | */ 11 | public interface ConvertedParameterTransformer { 12 | 13 | /** 14 | * Returns the parameters that will be passed to the method on invocation. 15 | * 16 | * @param target optional service name used to locate the target object 17 | * to invoke the Method on. 18 | * @param convertedParams the parameters to pass to the method on invocation. 19 | * @return the mutated parameters that will be passed to the method on invocation. 20 | */ 21 | Object[] transformConvertedParameters(Object target, Object[] convertedParams); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/spring/CompositeJsonServiceExporter.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring; 2 | 3 | import com.googlecode.jsonrpc4j.JsonRpcServer; 4 | import org.springframework.web.HttpRequestHandler; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | /** 12 | * A Composite service exporter for spring that exposes 13 | * multiple services via JSON-RPC over HTTP. 14 | */ 15 | @SuppressWarnings("unused") 16 | public class CompositeJsonServiceExporter extends AbstractCompositeJsonServiceExporter implements HttpRequestHandler { 17 | 18 | private JsonRpcServer jsonRpcServer; 19 | 20 | /** 21 | * {@inheritDoc} 22 | */ 23 | @Override 24 | protected void exportService() { 25 | jsonRpcServer = getJsonRpcServer(); 26 | } 27 | 28 | /** 29 | * {@inheritDoc} 30 | */ 31 | public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 32 | jsonRpcServer.handle(request, response); 33 | response.getOutputStream().flush(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/JsonResponse.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | 5 | /** 6 | * Contains the JSON-RPC answer in {@code response} 7 | * {@code exceptionToRethrow} contains exception, which should be thrown when property {@code rethrowExceptions} 8 | * is active 9 | */ 10 | public class JsonResponse { 11 | private JsonNode response; 12 | private int code; 13 | private RuntimeException exceptionToRethrow; 14 | 15 | public JsonResponse() { 16 | } 17 | 18 | public JsonResponse(JsonNode response, int code) { 19 | this.response = response; 20 | this.code = code; 21 | } 22 | 23 | public JsonNode getResponse() { 24 | return response; 25 | } 26 | 27 | public void setResponse(JsonNode response) { 28 | this.response = response; 29 | } 30 | 31 | public int getCode() { 32 | return code; 33 | } 34 | 35 | public void setCode(int code) { 36 | this.code = code; 37 | } 38 | 39 | public RuntimeException getExceptionToRethrow() { 40 | return exceptionToRethrow; 41 | } 42 | 43 | public void setExceptionToRethrow(RuntimeException exceptionToRethrow) { 44 | this.exceptionToRethrow = exceptionToRethrow; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/JsonRpcPathClientIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring; 2 | 3 | import com.googlecode.jsonrpc4j.spring.service.Service; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.aop.support.AopUtils; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 | 11 | import static org.junit.Assert.assertNotNull; 12 | import static org.junit.Assert.assertTrue; 13 | 14 | @RunWith(SpringJUnit4ClassRunner.class) 15 | @ContextConfiguration("classpath:clientApplicationContext.xml") 16 | public class JsonRpcPathClientIntegrationTest { 17 | 18 | @Autowired 19 | private Service service; 20 | 21 | @Test 22 | public void shouldCreateServiceExporter() { 23 | assertNotNull(service); 24 | assertTrue(AopUtils.isAopProxy(service)); 25 | } 26 | 27 | @Test 28 | public void callToObjectMethodsShouldBeHandledLocally() { 29 | if (service != null) { 30 | assertNotNull(service.toString()); 31 | // noinspection ResultOfMethodCallIgnored 32 | service.hashCode(); 33 | // noinspection EqualsWithItself 34 | assertTrue(service.equals(service)); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/JsonRpcPathServerIntegrationTest.java: -------------------------------------------------------------------------------- 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 static org.junit.Assert.assertNotNull; 11 | import static org.junit.Assert.assertSame; 12 | 13 | /** 14 | * @deprecated this test should be removed (replaced by {@link JsonRpcPathServerIntegrationTestB}) 15 | * once the {@link AutoJsonRpcServiceExporter} is dropped. 16 | */ 17 | 18 | @RunWith(SpringJUnit4ClassRunner.class) 19 | @ContextConfiguration("classpath:serverApplicationContext.xml") 20 | @Deprecated 21 | public class JsonRpcPathServerIntegrationTest { 22 | 23 | @Autowired 24 | private ApplicationContext applicationContext; 25 | 26 | @Test 27 | public void shouldCreateServiceExporter() { 28 | assertNotNull(applicationContext); 29 | 30 | { 31 | Object bean = applicationContext.getBean("/TestService"); 32 | assertSame(JsonServiceExporter.class, bean.getClass()); 33 | } 34 | 35 | { 36 | Object bean = applicationContext.getBean("/ServiceSansInterface"); 37 | assertSame(JsonServiceExporter.class, bean.getClass()); 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/spring/AutoJsonRpcServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.TYPE; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | /** 10 | * This annotation goes on the implementation of the JSON-RPC service. It defines any additional paths on 11 | * which the JSON-RPC service should be exported. This can be used with the {@link AutoJsonRpcServiceImplExporter} 12 | * in order to automatically expose the JSON-RPC services in a spring based web application server. Note that the 13 | * implementation should still continue to carry the {@link com.googlecode.jsonrpc4j.JsonRpcServer} annotation; 14 | * preferably on the service interface. 15 | */ 16 | 17 | @Target(TYPE) 18 | @Retention(RUNTIME) 19 | public @interface AutoJsonRpcServiceImpl { 20 | 21 | /** 22 | * This value may contain a list of additional paths that the JSON-RPC service will be exposed on. 23 | * These are in addition to any which are defined on the {@link com.googlecode.jsonrpc4j.JsonRpcService} 24 | * annotation preferably on the service interface. This might be used, for example, where you still want 25 | * to expose a service on legacy paths for older clients. 26 | * 27 | * @return an array of additional paths 28 | */ 29 | 30 | String[] additionalPaths() default {}; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/spring/JsonRpcPathClientIntegrationTestB.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring; 2 | 3 | import com.googlecode.jsonrpc4j.spring.serviceb.Temperature; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.aop.support.AopUtils; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 | 10 | import static org.junit.Assert.assertNotNull; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | @RunWith(SpringJUnit4ClassRunner.class) 14 | @ContextConfiguration("classpath:clientApplicationContextB.xml") 15 | public class JsonRpcPathClientIntegrationTestB { 16 | 17 | @JsonRpcReference(address = "http://localhost:8080") 18 | private Temperature temperature; 19 | 20 | public Integer demo() { 21 | return temperature.currentTemperature(); 22 | } 23 | 24 | 25 | @Test 26 | public void shouldCreateServiceExporter() { 27 | assertNotNull(temperature); 28 | assertTrue(AopUtils.isAopProxy(temperature)); 29 | } 30 | 31 | @Test 32 | public void callToObjectMethodsShouldBeHandledLocally() { 33 | if (temperature != null) { 34 | assertNotNull(temperature.toString()); 35 | // noinspection ResultOfMethodCallIgnored 36 | temperature.hashCode(); 37 | // noinspection EqualsWithItself 38 | assertTrue(temperature.equals(temperature)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/googlecode/jsonrpc4j/integration/SimpleTest.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.integration; 2 | 3 | import com.googlecode.jsonrpc4j.ProxyUtil; 4 | import com.googlecode.jsonrpc4j.util.BaseRestTest; 5 | import com.googlecode.jsonrpc4j.util.FakeServiceInterface; 6 | import com.googlecode.jsonrpc4j.util.FakeServiceInterfaceImpl; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | public class SimpleTest extends BaseRestTest { 14 | private FakeServiceInterface service; 15 | 16 | @Before 17 | @Override 18 | public void setup() throws Exception { 19 | super.setup(); 20 | service = ProxyUtil.createClientProxy(FakeServiceInterface.class, getClient()); 21 | } 22 | 23 | @Override 24 | protected Class service() { 25 | return FakeServiceInterfaceImpl.class; 26 | } 27 | 28 | @Test 29 | public void doSomething() { 30 | service.doSomething(); 31 | } 32 | 33 | @Test 34 | public void returnPrimitiveInt() { 35 | final int at = 22; 36 | assertEquals(at, service.returnPrimitiveInt(at)); 37 | } 38 | 39 | @Test 40 | public void returnCustomClass() { 41 | final int at = 22; 42 | final String message = "simple"; 43 | FakeServiceInterface.CustomClass result = service.returnCustomClass(at, message); 44 | assertEquals(at, result.integer); 45 | assertEquals(message, result.string); 46 | assertTrue(result.list.contains(message)); 47 | assertTrue(result.list.contains("" + at)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/HttpStatusCodeProvider.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j; 2 | 3 | /** 4 | *
5 | * A status code provider maps an HTTP status code to a JSON-RPC result code (e.g. -32000 -> 500). 6 | *
7 | *8 | * From version 2.0 on the JSON-RPC specification is not explicitly documenting the mapping of result/error codes, so 9 | * this provider can be used to configure application specific HTTP status codes for a given JSON-RPC error code. 10 | *
11 | *12 | * The default implementation {@link DefaultHttpStatusCodeProvider} follows the rules defined in the 13 | * JSON-RPC over HTTP document. 14 | *
15 | */ 16 | public interface HttpStatusCodeProvider { 17 | 18 | /** 19 | * Returns an HTTP status code for the given response and result code. 20 | * 21 | * @param resultCode the result code of the current JSON-RPC method call. This is used to look up the HTTP status 22 | * code. 23 | * @return the int representation of the HTTP status code that should be used by the JSON-RPC response. 24 | */ 25 | int getHttpStatusCode(int resultCode); 26 | 27 | /** 28 | * Returns result code for the given HTTP status code 29 | * 30 | * @param httpStatusCode the int representation of the HTTP status code that should be used by the JSON-RPC response. 31 | * @return resultCode the result code of the current JSON-RPC method call. This is used to look up the HTTP status 32 | */ 33 | Integer getJsonRpcCode(int httpStatusCode); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/spring/rest/SslClientHttpRequestFactory.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j.spring.rest; 2 | 3 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 4 | 5 | import javax.net.ssl.HostnameVerifier; 6 | import javax.net.ssl.HttpsURLConnection; 7 | import javax.net.ssl.SSLContext; 8 | import java.io.IOException; 9 | import java.net.HttpURLConnection; 10 | 11 | /** 12 | * Implementation of {@link org.springframework.http.client.ClientHttpRequestFactory} that creates HTTPS connection 13 | * with specified settings. 14 | */ 15 | class SslClientHttpRequestFactory 16 | extends SimpleClientHttpRequestFactory { 17 | 18 | private SSLContext sslContext; 19 | private HostnameVerifier hostNameVerifier; 20 | 21 | @Override 22 | protected void prepareConnection(HttpURLConnection connection, String httpMethod) 23 | throws IOException { 24 | 25 | if (connection instanceof HttpsURLConnection) { 26 | final HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; 27 | 28 | if (hostNameVerifier != null) { 29 | httpsConnection.setHostnameVerifier(hostNameVerifier); 30 | } 31 | 32 | if (sslContext != null) { 33 | httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory()); 34 | } 35 | } 36 | 37 | super.prepareConnection(connection, httpMethod); 38 | } 39 | 40 | public void setSslContext(SSLContext sslContext) { 41 | this.sslContext = sslContext; 42 | } 43 | 44 | public void setHostNameVerifier(HostnameVerifier hostNameVerifier) { 45 | this.hostNameVerifier = hostNameVerifier; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/googlecode/jsonrpc4j/InvocationListener.java: -------------------------------------------------------------------------------- 1 | package com.googlecode.jsonrpc4j; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.List; 7 | 8 | /** 9 | * Implementations of this interface are able to be informed when JSON-RPC services are invoked. This allows for 10 | * instrumentation of the invocations so that statistics about the invocations can be recorded and reported on. 11 | * 12 | * @author Andrew Lindesay 13 | */ 14 | 15 | public interface InvocationListener { 16 | 17 | /** 18 | * This method will be invoked prior to a JSON-RPC service being invoked. 19 | * 20 | * @param method is the method that will be invoked. 21 | * @param arguments are the arguments that will be passed to the method when it is invoked. 22 | */ 23 | 24 | void willInvoke(Method method, ListThis test replaces {@link JsonRpcPathServerIntegrationTest} and uses the new class 25 | * {@link AutoJsonRpcServiceImplExporter} which is designed to vend specific annotated 26 | * implementations.
27 | */ 28 | 29 | @RunWith(SpringJUnit4ClassRunner.class) 30 | @ContextConfiguration("classpath:serverApplicationContextB.xml") 31 | public class JsonRpcPathServerIntegrationTestB { 32 | 33 | @Autowired 34 | private ApplicationContext applicationContext; 35 | 36 | @Test 37 | public void testExportedService() { 38 | assertNotNull(applicationContext); 39 | 40 | // check that the service was vended on both paths that were configured. 41 | 42 | { 43 | Object bean = applicationContext.getBean("/api/temperature"); 44 | assertSame(JsonServiceExporter.class, bean.getClass()); 45 | } 46 | 47 | { 48 | Object bean = applicationContext.getBean("/api-web/temperature"); 49 | assertSame(JsonServiceExporter.class, bean.getClass()); 50 | } 51 | 52 | // check that the bean was only exported on the two paths provided. 53 | 54 | { 55 | String[] names = applicationContext.getBeanNamesForType(JsonServiceExporter.class); 56 | Arrays.sort(names); 57 | assertSame(2, names.length); 58 | assertEquals("/api-web/temperature", names[0]); 59 | assertEquals("/api/temperature", names[1]); 60 | } 61 | 62 | // check that the no-op was also successfully configured in the context. 63 | 64 | { 65 | Map13 | * Interceptors could throw {@link RuntimeException}, in this case JSON RPC standard error will be shown, except preHandleJson. 14 | * In case preHandleJson throws exception, it will generate HTTP code 400 "Bad Request" with empty body. So be careful. 15 | * @author pavyazankin 16 | * @since 21/09/2017 17 | */ 18 | public interface JsonRpcInterceptor { 19 | 20 | /** 21 | * Method to intercept raw JSON RPC json objects. Don't throw exceptions here. 22 | * In case preHandleJson throws exception, it will be HTTP code 400 "Bad Request" with empty body. So be careful. 23 | * @param json raw JSON RPC request json 24 | */ 25 | void preHandleJson(JsonNode json); 26 | 27 | /** 28 | * If exception will be thrown in this method, standard JSON RPC error will be generated. 29 | *
Example 30 | *
31 | * {
32 | * "jsonrpc":"2.0",
33 | * "id":0,
34 | * "error":{
35 | * "code":-32001,
36 | * "message":"123",
37 | * "data":{
38 | * "exceptionTypeName":"java.lang.RuntimeException",
39 | * "message":"123"
40 | * }
41 | * }
42 | * }
43 | *
44 | * 45 | * For changing exception handling custom {@link ErrorResolver} could be generated. 46 | *
47 | * @param target target service 48 | * @param method target method 49 | * @param params list of params as {@link JsonNode}s 50 | */ 51 | void preHandle(Object target, Method method, List
15 | * {
16 | * "jsonrpc": "2.0",
17 | * "method": "service.method",
18 | * "params": {"foo", "bar"},
19 | * "id": 1
20 | * }
21 | *
22 | * An example of using this class is:
23 | *
24 | * JsonRpcMultiServer rpcServer = new JsonRpcMultiServer();
25 | * rpcServer.addService("Foo", new FooService())
26 | * .addService("Bar", new BarService());
27 | *
28 | * A client can then call a test(String, String) method on the Foo service
29 | * like this:
30 | *
31 | * {
32 | * "jsonrpc": "2.0",
33 | * "method": "Foo.test",
34 | * "params": ["some", "thing"],
35 | * "id": 1
36 | * }
37 | *
38 | */
39 | @SuppressWarnings({"WeakerAccess", "unused"})
40 | public class JsonRpcMultiServer extends JsonRpcServer {
41 |
42 | public static final char DEFAULT_SEPARATOR = '.';
43 | private static final Logger logger = LoggerFactory.getLogger(JsonRpcMultiServer.class);
44 |
45 | private final Mapnull
106 | */
107 | @Override
108 | protected String getServiceName(final String methodName) {
109 | if (methodName != null) {
110 | int ndx = methodName.indexOf(this.separator);
111 | if (ndx > 0) {
112 | return methodName.substring(0, ndx);
113 | }
114 | }
115 | return methodName;
116 | }
117 |
118 | /**
119 | * Get the method name from the methodNode, stripping off the service name.
120 | *
121 | * @param methodName the JsonNode for the method
122 | * @return the name of the method that should be invoked
123 | */
124 | @Override
125 | protected String getMethodName(final String methodName) {
126 | if (methodName != null) {
127 | int ndx = methodName.indexOf(this.separator);
128 | if (ndx > 0) {
129 | return methodName.substring(ndx + 1);
130 | }
131 | }
132 | return methodName;
133 | }
134 |
135 | /**
136 | * Get the handler (object) that should be invoked to execute the specified
137 | * RPC method based on the specified service name.
138 | *
139 | * @param serviceName the service name
140 | * @return the handler to invoke the RPC call against
141 | */
142 | @Override
143 | protected Object getHandler(String serviceName) {
144 | Object handler = handlerMap.get(serviceName);
145 | if (handler == null) {
146 | logger.error("Service '{}' is not registered in this multi-server", serviceName);
147 | throw new RuntimeException("Service '" + serviceName + "' does not exist");
148 | }
149 | return handler;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsonrpc4j/spring/AutoJsonRpcClientProxyFactory.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsonrpc4j.spring;
2 |
3 | import com.googlecode.jsonrpc4j.JsonRpcService;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.BeansException;
7 | import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
8 | import org.springframework.context.ApplicationContext;
9 | import org.springframework.context.ApplicationContextAware;
10 | import org.springframework.core.type.AnnotationMetadata;
11 | import org.springframework.core.type.ClassMetadata;
12 | import org.springframework.core.type.classreading.MetadataReader;
13 | import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
14 | import org.springframework.util.ReflectionUtils;
15 |
16 | import java.io.IOException;
17 | import java.net.MalformedURLException;
18 | import java.net.URL;
19 |
20 |
21 | public class AutoJsonRpcClientProxyFactory implements ApplicationContextAware, InstantiationAwareBeanPostProcessor {
22 |
23 | private static final Logger logger = LoggerFactory.getLogger(AutoJsonRpcClientProxyFactory.class);
24 |
25 | private ApplicationContext applicationContext;
26 |
27 | private URL baseUrl;
28 |
29 | @Override
30 | public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
31 | ReflectionUtils.doWithFields(bean.getClass(), field -> {
32 | if (field.isAnnotationPresent(JsonRpcReference.class)) {
33 | // valid
34 | final Class serviceInterface = field.getType();
35 | if (!serviceInterface.isInterface()) {
36 | throw new RuntimeException("JsonRpcReference must be interface.");
37 | }
38 |
39 | JsonRpcReference rpcReference = field.getAnnotation(JsonRpcReference.class);
40 | String fullUrl = fullUrl(rpcReference.address(), serviceInterface);
41 | JsonProxyFactoryBean factoryBean = createJsonBean(serviceInterface, fullUrl);
42 | factoryBean.afterPropertiesSet();
43 | // get proxy
44 | Object proxy = null;
45 | try {
46 | proxy = factoryBean.getObject();
47 | } catch (Exception e) {
48 | throw new RuntimeException(e);
49 | }
50 | // set bean
51 | field.setAccessible(true);
52 | field.set(bean, proxy);
53 | logger.debug("------------- set bean {} field {} to proxy {}", beanName, field.getName(), proxy.getClass().getName());
54 | }
55 | });
56 | return true;
57 | }
58 |
59 | private String fullUrl(String url, Class serviceInterface) {
60 | try {
61 | baseUrl = new URL(url);
62 | } catch (MalformedURLException e) {
63 | throw new RuntimeException(e);
64 | }
65 | SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(applicationContext);
66 | MetadataReader metadataReader = null;
67 | try {
68 | metadataReader = metadataReaderFactory.getMetadataReader(serviceInterface.getName());
69 | } catch (IOException e) {
70 | throw new RuntimeException(e);
71 | }
72 | ClassMetadata classMetadata = metadataReader.getClassMetadata();
73 | AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
74 | String jsonRpcPathAnnotation = JsonRpcService.class.getName();
75 | if (annotationMetadata.isAnnotated(jsonRpcPathAnnotation)) {
76 | String className = classMetadata.getClassName();
77 | String path = (String) annotationMetadata.getAnnotationAttributes(jsonRpcPathAnnotation).get("value");
78 | logger.debug("Found JSON-RPC service to proxy [{}] on path '{}'.", className, path);
79 | return appendBasePath(path);
80 | } else {
81 | throw new RuntimeException("JsonRpcService must be interface.");
82 | }
83 |
84 | }
85 |
86 | private String appendBasePath(String path) {
87 | try {
88 | return new URL(baseUrl, path).toString();
89 | } catch (MalformedURLException e) {
90 | throw new IllegalArgumentException(String.format("Cannot combine URLs '%s' and '%s' to valid URL.", baseUrl, path), e);
91 | }
92 | }
93 |
94 |
95 | private JsonProxyFactoryBean createJsonBean(Class serviceInterface, String serviceUrl) {
96 | JsonProxyFactoryBean bean = new JsonProxyFactoryBean();
97 | bean.setServiceInterface(serviceInterface);
98 | bean.setServiceUrl(serviceUrl);
99 | return bean;
100 | }
101 |
102 | @Override
103 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
104 | this.applicationContext = applicationContext;
105 | }
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/src/test/java/com/googlecode/jsonrpc4j/server/HttpStatusCodeProviderTest.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsonrpc4j.server;
2 |
3 | import com.googlecode.jsonrpc4j.HttpStatusCodeProvider;
4 | import com.googlecode.jsonrpc4j.JsonRpcServer;
5 | import org.easymock.EasyMockRunner;
6 | import org.easymock.Mock;
7 | import org.easymock.MockType;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 |
12 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.BULK_ERROR;
13 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.ERROR_NOT_HANDLED;
14 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.INTERNAL_ERROR;
15 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.INVALID_REQUEST;
16 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.METHOD_NOT_FOUND;
17 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.METHOD_PARAMS_INVALID;
18 | import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.PARSE_ERROR;
19 | import static com.googlecode.jsonrpc4j.server.DefaultHttpStatusCodeProviderTest.assertHttpStatusCodeForJsonRpcRequest;
20 | import static com.googlecode.jsonrpc4j.util.Util.intParam1;
21 | import static com.googlecode.jsonrpc4j.util.Util.intParam2;
22 | import static com.googlecode.jsonrpc4j.util.Util.invalidJsonRpcRequestStream;
23 | import static com.googlecode.jsonrpc4j.util.Util.invalidJsonStream;
24 | import static com.googlecode.jsonrpc4j.util.Util.mapper;
25 | import static com.googlecode.jsonrpc4j.util.Util.messageWithListParams;
26 | import static com.googlecode.jsonrpc4j.util.Util.messageWithListParamsStream;
27 | import static com.googlecode.jsonrpc4j.util.Util.multiMessageOfStream;
28 | import static com.googlecode.jsonrpc4j.util.Util.param1;
29 | import static com.googlecode.jsonrpc4j.util.Util.param2;
30 |
31 | /**
32 | * These tests validate the functionality of a custom HttpStatusCodeProvider implementation.
33 | */
34 | @RunWith(EasyMockRunner.class)
35 | public class HttpStatusCodeProviderTest {
36 |
37 | @Mock(type = MockType.NICE)
38 | private JsonRpcBasicServerTest.ServiceInterface mockService;
39 | private JsonRpcServer jsonRpcServer;
40 | private HttpStatusCodeProvider httpStatusCodeProvider;
41 |
42 | @Before
43 | public void setUp() throws Exception {
44 | jsonRpcServer = new JsonRpcServer(mapper, mockService, JsonRpcBasicServerTest.ServiceInterface.class);
45 | httpStatusCodeProvider = new HttpStatusCodeProvider() {
46 | @Override
47 | public int getHttpStatusCode(int resultCode) {
48 | if (resultCode == PARSE_ERROR.code) {
49 | return 1002;
50 | } else if (resultCode == INVALID_REQUEST.code) {
51 | return 1001;
52 | } else if (resultCode == METHOD_NOT_FOUND.code) {
53 | return 1003;
54 | } else if (resultCode == METHOD_PARAMS_INVALID.code) {
55 | return 1004;
56 | } else if (resultCode == INTERNAL_ERROR.code) {
57 | return 1007;
58 | } else if (resultCode == ERROR_NOT_HANDLED.code) {
59 | return 1006;
60 | } else if (resultCode == BULK_ERROR.code) {
61 | return 1005;
62 | } else {
63 | return 1000;
64 | }
65 | }
66 |
67 | @Override
68 | public Integer getJsonRpcCode(int httpStatusCode) {
69 | return null;
70 | }
71 | };
72 |
73 | jsonRpcServer.setHttpStatusCodeProvider(httpStatusCodeProvider);
74 | }
75 |
76 | @Test
77 | public void http1001ForInvalidRequest() throws Exception {
78 | assertHttpStatusCodeForJsonRpcRequest(invalidJsonRpcRequestStream(), 1003, jsonRpcServer);
79 | }
80 |
81 | @Test
82 | public void http1002ForParseError() throws Exception {
83 | assertHttpStatusCodeForJsonRpcRequest(invalidJsonStream(), 1002, jsonRpcServer);
84 | }
85 |
86 | @Test
87 | public void http1000ForValidRequest() throws Exception {
88 | assertHttpStatusCodeForJsonRpcRequest(messageWithListParamsStream(1, "testMethod", param1), 1000, jsonRpcServer);
89 | }
90 |
91 | @Test
92 | public void http1003ForNonExistingMethod() throws Exception {
93 | assertHttpStatusCodeForJsonRpcRequest(messageWithListParamsStream(1, "nonExistingMethod", param1), 1003, jsonRpcServer);
94 | }
95 |
96 | @Test
97 | public void http1004ForInvalidMethodParameters() throws Exception {
98 | assertHttpStatusCodeForJsonRpcRequest(messageWithListParamsStream(1, "testMethod", param1, param2), 1004, jsonRpcServer);
99 | }
100 |
101 | @Test
102 | public void http1005ForBulkErrors() throws Exception {
103 | assertHttpStatusCodeForJsonRpcRequest(
104 | multiMessageOfStream(
105 | messageWithListParams(1, "testMethod", param1, param2),
106 | messageWithListParams(2, "overloadedMethod", intParam1, intParam2)
107 | ), 1005, jsonRpcServer);
108 | }
109 |
110 | @Test
111 | public void http1006ForErrorNotHandled() throws Exception {
112 | JsonRpcServer server = new JsonRpcServer(mapper, mockService, JsonRpcErrorsTest.ServiceInterfaceWithoutAnnotation.class);
113 | server.setHttpStatusCodeProvider(httpStatusCodeProvider);
114 | assertHttpStatusCodeForJsonRpcRequest(messageWithListParamsStream(1, "testMethod"), 1006, server);
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/publishing.gradle:
--------------------------------------------------------------------------------
1 | import io.codearte.gradle.nexus.BaseStagingTask
2 | import io.codearte.gradle.nexus.NexusStagingPlugin
3 |
4 | import javax.naming.ConfigurationException
5 |
6 | buildscript {
7 | repositories {
8 | gradlePluginPortal()
9 | }
10 | dependencies {
11 | classpath 'io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0'
12 | classpath 'gradle.plugin.net.wooga.gradle:atlas-github:1.0.1'
13 | classpath 'net.researchgate:gradle-release:2.7.0'
14 | classpath 'org.ajoberstar:grgit:2.3.0'
15 | classpath 'org.kohsuke:github-api:1.93'
16 | }
17 | }
18 |
19 | ext {
20 | it.'signing.secretKeyRingFile' = project.findProperty('jsonrpc4j.signing.secretKeyRingFile') ?:
21 | project.findProperty('signing.secretKeyRingFile')
22 | it.'signing.password' = project.findProperty('jsonrpc4j.signing.password') ?:
23 | project.findProperty('signing.password')
24 | it.'signing.keyId' = project.findProperty('jsonrpc4j.signing.keyId') ?:
25 | project.findProperty('signing.keyId')
26 | sonatypeUsername = project.findProperty('jsonrpc4j.sonatype.username') ?:
27 | project.findProperty('sonatype.username')
28 | sonatypePassword = project.findProperty('jsonrpc4j.sonatype.password') ?:
29 | project.findProperty('sonatype.password')
30 | sonatypeStagingProfileId = project.findProperty('jsonrpc4j.sonatype.stagingProfileId') ?:
31 | project.findProperty('sonatype.stagingProfileId')
32 | it.'github.token' = project.findProperty('jsonrpc4j.github.token') ?:
33 | project.findProperty('github.token')
34 | }
35 |
36 | allprojects {
37 | apply plugin: 'maven-publish'
38 |
39 | publishing {
40 | repositories {
41 | maven {
42 | name 'sonatype'
43 | if (releaseVersion) {
44 | url 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
45 | } else {
46 | url 'https://oss.sonatype.org/content/repositories/snapshots/'
47 | }
48 | credentials {
49 | username sonatypeUsername
50 | password sonatypePassword
51 | }
52 | }
53 | }
54 | }
55 |
56 | tasks.withType(PublishToMavenRepository) {
57 | doFirst {
58 | if (!sonatypeUsername) {
59 | throw new ConfigurationException(
60 | 'Please set the Sonatype username with project property "sonatype.username" ' +
61 | 'or "jsonrpc4j.sonatype.username". If both are set, the latter will be effective.')
62 | }
63 | if (!sonatypePassword) {
64 | throw new ConfigurationException(
65 | 'Please set the Sonatype password with project property "sonatype.password" ' +
66 | 'or "jsonrpc4j.sonatype.password". If both are set, the latter will be effective.')
67 | }
68 | }
69 | }
70 | }
71 |
72 | publishing {
73 | publications {
74 | jsonrpc4j(MavenPublication) {
75 | from components.java
76 | artifact documentationJar
77 | artifact sourcesJar
78 | }
79 | }
80 | }
81 |
82 | apply from: 'pom.gradle'
83 |
84 | allprojects {
85 | apply plugin: 'signing'
86 |
87 | signing {
88 | required {
89 | // signing is required if this is a release version and the artifacts are to be published
90 | releaseVersion && tasks.withType(PublishToMavenRepository).find {
91 | gradle.taskGraph.hasTask it
92 | }
93 | }
94 | sign publishing.publications
95 | }
96 | }
97 |
98 | apply plugin: NexusStagingPlugin
99 | // remove superfluous tasks from NexusStagingPlugin
100 | //tasks.removeAll([promoteRepository, closeAndPromoteRepository, getStagingProfile])
101 |
102 | nexusStaging {
103 | stagingProfileId sonatypeStagingProfileId
104 | username sonatypeUsername
105 | password sonatypePassword
106 | }
107 |
108 | // make sure the staging tasks are run after any publishing tasks if both are to be run
109 | tasks.withType(BaseStagingTask) {
110 | mustRunAfter allprojects.tasks*.withType(PublishToMavenRepository)
111 |
112 | doFirst {
113 | if (!sonatypeStagingProfileId) {
114 | throw new ConfigurationException(
115 | 'Please set the Sonatype staging profile id with project property "sonatype.stagingProfileId" ' +
116 | 'or "jsonrpc4j.sonatype.stagingProfileId". If both are set, the latter will be effective.')
117 | }
118 | if (!sonatypeUsername) {
119 | throw new ConfigurationException(
120 | 'Please set the Sonatype username with project property "sonatype.username" ' +
121 | 'or "jsonrpc4j.sonatype.username". If both are set, the latter will be effective.')
122 | }
123 | if (!sonatypePassword) {
124 | throw new ConfigurationException(
125 | 'Please set the Sonatype password with project property "sonatype.password" ' +
126 | 'or "jsonrpc4j.sonatype.password". If both are set, the latter will be effective.')
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/test/java/com/googlecode/jsonrpc4j/server/JsonRpcErrorsTest.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsonrpc4j.server;
2 |
3 | import com.fasterxml.jackson.databind.JsonNode;
4 | import com.googlecode.jsonrpc4j.ErrorResolver;
5 | import com.googlecode.jsonrpc4j.JsonRpcBasicServer;
6 | import com.googlecode.jsonrpc4j.JsonRpcError;
7 | import com.googlecode.jsonrpc4j.JsonRpcErrors;
8 | import com.googlecode.jsonrpc4j.util.CustomTestException;
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | import java.io.ByteArrayOutputStream;
13 |
14 | import static com.googlecode.jsonrpc4j.util.Util.error;
15 | import static com.googlecode.jsonrpc4j.util.Util.errorCode;
16 | import static com.googlecode.jsonrpc4j.util.Util.errorData;
17 | import static com.googlecode.jsonrpc4j.util.Util.errorMessage;
18 | import static com.googlecode.jsonrpc4j.util.Util.exceptionType;
19 | import static com.googlecode.jsonrpc4j.util.Util.mapper;
20 | import static com.googlecode.jsonrpc4j.util.Util.messageWithListParamsStream;
21 | import static org.junit.Assert.assertEquals;
22 | import static org.junit.Assert.assertNotNull;
23 |
24 | /**
25 | * For testing the @JsonRpcErrors and @JsonRpcError annotations
26 | */
27 | public class JsonRpcErrorsTest {
28 |
29 | private ByteArrayOutputStream byteArrayOutputStream;
30 | private CustomTestException testException;
31 | private CustomTestException testExceptionWithMessage;
32 |
33 | @Before
34 | public void setup() {
35 | byteArrayOutputStream = new ByteArrayOutputStream();
36 | testException = new CustomTestException();
37 | testExceptionWithMessage = new CustomTestException("exception message");
38 | }
39 |
40 | @Test
41 | public void exceptionWithoutAnnotatedServiceInterface() throws Exception {
42 | JsonRpcBasicServer jsonRpcServer = new JsonRpcBasicServer(mapper, new Service(), ServiceInterfaceWithoutAnnotation.class);
43 | jsonRpcServer.handleRequest(messageWithListParamsStream(1, "testMethod"), byteArrayOutputStream);
44 | JsonNode error = error(byteArrayOutputStream);
45 | assertNotNull(error);
46 | assertEquals(ErrorResolver.JsonError.ERROR_NOT_HANDLED.code, errorCode(error).intValue());
47 | }
48 |
49 | @Test
50 | public void exceptionWithAnnotatedServiceInterface() throws Exception {
51 | JsonRpcBasicServer jsonRpcServer = new JsonRpcBasicServer(mapper, new Service(), ServiceInterfaceWithAnnotation.class);
52 | jsonRpcServer.handleRequest(messageWithListParamsStream(1, "testMethod"), byteArrayOutputStream);
53 |
54 | JsonNode error = error(byteArrayOutputStream);
55 | assertNotNull(error);
56 | assertEquals(1234, errorCode(error).intValue());
57 | assertEquals(null, errorMessage(error).textValue());
58 | JsonNode data = errorData(error);
59 | assertNotNull(data);
60 | assertEquals(null, errorMessage(data).textValue());
61 | assertEquals(CustomTestException.class.getName(), exceptionType(data).textValue());
62 | }
63 |
64 | @Test
65 | public void exceptionWithAnnotatedServiceInterfaceMessageAndData() throws Exception {
66 | JsonRpcBasicServer jsonRpcServer = new JsonRpcBasicServer(mapper, new Service(), ServiceInterfaceWithAnnotationMessageAndData.class);
67 | jsonRpcServer.handleRequest(messageWithListParamsStream(1, "testMethod"), byteArrayOutputStream);
68 |
69 | JsonNode error = error(byteArrayOutputStream);
70 |
71 | assertNotNull(error);
72 | assertEquals(-5678, errorCode(error).intValue());
73 | assertEquals("The message", errorMessage(error).textValue());
74 | }
75 |
76 | @Test
77 | public void exceptionWithMsgInException() throws Exception {
78 | JsonRpcBasicServer jsonRpcServer = new JsonRpcBasicServer(mapper, new ServiceWithExceptionMsg(), ServiceInterfaceWithAnnotation.class);
79 | jsonRpcServer.handleRequest(messageWithListParamsStream(1, "testMethod"), byteArrayOutputStream);
80 |
81 | JsonNode error = error(byteArrayOutputStream);
82 |
83 | assertNotNull(error);
84 | assertEquals(1234, errorCode(error).intValue());
85 | assertEquals(testExceptionWithMessage.getMessage(), errorMessage(error).textValue());
86 | JsonNode data = errorData(error);
87 | assertNotNull(data);
88 | assertEquals(testExceptionWithMessage.getMessage(), errorMessage(data).textValue());
89 | assertEquals(CustomTestException.class.getName(), exceptionType(data).textValue());
90 | }
91 |
92 | @SuppressWarnings({"unused", "WeakerAccess"})
93 | public interface ServiceInterfaceWithoutAnnotation {
94 | Object testMethod();
95 | }
96 |
97 | @SuppressWarnings({"unused", "WeakerAccess"})
98 | public interface ServiceInterfaceWithAnnotation {
99 | @JsonRpcErrors({@JsonRpcError(exception = CustomTestException.class, code = 1234)})
100 | Object testMethod();
101 | }
102 |
103 | @SuppressWarnings({"unused", "WeakerAccess"})
104 | public interface ServiceInterfaceWithAnnotationMessageAndData {
105 | @JsonRpcErrors({@JsonRpcError(exception = CustomTestException.class, code = -5678, message = "The message", data = "The data")})
106 | Object testMethod();
107 | }
108 |
109 | private class Service implements ServiceInterfaceWithoutAnnotation, ServiceInterfaceWithAnnotation, ServiceInterfaceWithAnnotationMessageAndData {
110 | public Object testMethod() {
111 | throw testException;
112 | }
113 | }
114 |
115 | private class ServiceWithExceptionMsg implements ServiceInterfaceWithoutAnnotation, ServiceInterfaceWithAnnotation, ServiceInterfaceWithAnnotationMessageAndData {
116 | public Object testMethod() {
117 | throw testExceptionWithMessage;
118 | }
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jsonrpc4j/spring/AutoJsonRpcClientProxyCreator.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsonrpc4j.spring;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.googlecode.jsonrpc4j.JsonRpcService;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
8 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
9 | import org.springframework.beans.factory.support.BeanDefinitionBuilder;
10 | import org.springframework.beans.factory.support.DefaultListableBeanFactory;
11 | import org.springframework.context.ApplicationContext;
12 | import org.springframework.context.ApplicationContextAware;
13 | import org.springframework.context.EnvironmentAware;
14 | import org.springframework.core.env.Environment;
15 | import org.springframework.core.io.Resource;
16 | import org.springframework.core.type.AnnotationMetadata;
17 | import org.springframework.core.type.ClassMetadata;
18 | import org.springframework.core.type.classreading.MetadataReader;
19 | import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
20 |
21 | import java.io.IOException;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 |
25 | import static java.lang.String.format;
26 | import static org.springframework.util.ClassUtils.convertClassNameToResourcePath;
27 | import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
28 |
29 | /**
30 | * Auto-creates proxies for service interfaces annotated with {@link JsonRpcService}.
31 | */
32 | @SuppressWarnings("unused")
33 | public class AutoJsonRpcClientProxyCreator implements BeanFactoryPostProcessor, ApplicationContextAware, EnvironmentAware {
34 |
35 | private static final Logger logger = LoggerFactory.getLogger(AutoJsonRpcClientProxyCreator.class);
36 | private ApplicationContext applicationContext;
37 | private Environment environment;
38 | private String scanPackage;
39 | private URL baseUrl;
40 | private ObjectMapper objectMapper;
41 | private String contentType;
42 |
43 | @Override
44 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
45 | SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(applicationContext);
46 | DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
47 | String resolvedPath = resolvePackageToScan();
48 | logger.debug("Scanning '{}' for JSON-RPC service interfaces.", resolvedPath);
49 | try {
50 | for (Resource resource : applicationContext.getResources(resolvedPath)) {
51 | if (resource.isReadable()) {
52 | MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
53 | ClassMetadata classMetadata = metadataReader.getClassMetadata();
54 | AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
55 | String jsonRpcPathAnnotation = JsonRpcService.class.getName();
56 | if (annotationMetadata.isAnnotated(jsonRpcPathAnnotation)) {
57 | String className = classMetadata.getClassName();
58 | String path = (String) annotationMetadata.getAnnotationAttributes(jsonRpcPathAnnotation).get("value");
59 | path = this.environment.resolvePlaceholders(path);
60 | logger.debug("Found JSON-RPC service to proxy [{}] on path '{}'.", className, path);
61 | registerJsonProxyBean(defaultListableBeanFactory, className, path);
62 | }
63 | }
64 | }
65 | } catch (IOException e) {
66 | throw new IllegalStateException(format("Cannot scan package '%s' for classes.", resolvedPath), e);
67 | }
68 | }
69 |
70 | /**
71 | * Converts the scanPackage to something that the resource loader can handleRequest.
72 | */
73 | private String resolvePackageToScan() {
74 | return CLASSPATH_URL_PREFIX + convertClassNameToResourcePath(scanPackage) + "/**/*.class";
75 | }
76 |
77 | /**
78 | * Registers a new proxy bean with the bean factory.
79 | */
80 | private void registerJsonProxyBean(DefaultListableBeanFactory defaultListableBeanFactory, String className, String path) {
81 | BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
82 | .rootBeanDefinition(JsonProxyFactoryBean.class)
83 | .addPropertyValue("serviceUrl", appendBasePath(path))
84 | .addPropertyValue("serviceInterface", className);
85 |
86 | if (objectMapper != null) {
87 | beanDefinitionBuilder.addPropertyValue("objectMapper", objectMapper);
88 | }
89 |
90 | if (contentType != null) {
91 | beanDefinitionBuilder.addPropertyValue("contentType", contentType);
92 | }
93 |
94 | defaultListableBeanFactory.registerBeanDefinition(className + "-clientProxy", beanDefinitionBuilder.getBeanDefinition());
95 | }
96 |
97 | /**
98 | * Appends the base path to the path found in the interface.
99 | */
100 | private String appendBasePath(String path) {
101 | try {
102 | return new URL(baseUrl, path).toString();
103 | } catch (MalformedURLException e) {
104 | throw new IllegalArgumentException(format("Cannot combine URLs '%s' and '%s' to valid URL.", baseUrl, path), e);
105 | }
106 | }
107 |
108 | @Override
109 | public void setApplicationContext(ApplicationContext applicationContext) {
110 | this.applicationContext = applicationContext;
111 | }
112 |
113 |
114 | @Override
115 | public void setEnvironment(Environment environment) {
116 | this.environment = environment;
117 | }
118 |
119 | public void setBaseUrl(URL baseUrl) {
120 | this.baseUrl = baseUrl;
121 | }
122 |
123 | public void setScanPackage(String scanPackage) {
124 | this.scanPackage = scanPackage;
125 | }
126 |
127 | public void setObjectMapper(ObjectMapper objectMapper) {
128 | this.objectMapper = objectMapper;
129 | }
130 |
131 | public void setContentType(String contextType) {
132 | this.contentType = contextType;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/test/java/com/googlecode/jsonrpc4j/integration/ServerClientTest.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jsonrpc4j.integration;
2 |
3 | import com.fasterxml.jackson.databind.JsonNode;
4 | import com.googlecode.jsonrpc4j.ProxyUtil;
5 | import com.googlecode.jsonrpc4j.RequestInterceptor;
6 | import com.googlecode.jsonrpc4j.util.LocalThreadServer;
7 | import com.googlecode.jsonrpc4j.util.TestThrowable;
8 | import org.easymock.EasyMock;
9 | import org.easymock.EasyMockRunner;
10 | import org.easymock.Mock;
11 | import org.easymock.MockType;
12 | import org.junit.After;
13 | import org.junit.Before;
14 | import org.junit.Rule;
15 | import org.junit.Test;
16 | import org.junit.rules.ExpectedException;
17 | import org.junit.runner.RunWith;
18 |
19 | import static com.googlecode.jsonrpc4j.util.Util.nonAsciiCharacters;
20 | import static com.googlecode.jsonrpc4j.util.Util.param1;
21 | import static com.googlecode.jsonrpc4j.util.Util.param3;
22 | import static org.junit.Assert.assertEquals;
23 | import static org.junit.Assert.assertNotNull;
24 |
25 | @RunWith(EasyMockRunner.class)
26 | public class ServerClientTest {
27 |
28 | @Rule
29 | public final ExpectedException expectedEx = ExpectedException.none();
30 |
31 | @Mock(type = MockType.NICE)
32 | private Service mockService;
33 |
34 | private Service client;
35 | private LocalThreadServer