{"id":18634350,"url":"https://github.com/goldmansachs/jrpip","last_synced_at":"2025-04-11T07:33:10.645Z","repository":{"id":48912423,"uuid":"87099236","full_name":"goldmansachs/jrpip","owner":"goldmansachs","description":"JrPip is a Java Remote Proxy Invocation Provider.","archived":false,"fork":false,"pushed_at":"2022-07-24T15:17:02.000Z","size":247,"stargazers_count":44,"open_issues_count":0,"forks_count":20,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-25T11:11:20.333Z","etag":null,"topics":["java","rmi"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/goldmansachs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-03T17:03:10.000Z","updated_at":"2024-09-15T03:15:40.000Z","dependencies_parsed_at":"2022-09-02T07:12:15.462Z","dependency_job_id":null,"html_url":"https://github.com/goldmansachs/jrpip","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goldmansachs%2Fjrpip","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goldmansachs%2Fjrpip/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goldmansachs%2Fjrpip/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goldmansachs%2Fjrpip/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goldmansachs","download_url":"https://codeload.github.com/goldmansachs/jrpip/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248358874,"owners_count":21090447,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","rmi"],"created_at":"2024-11-07T05:18:17.894Z","updated_at":"2025-04-11T07:33:09.589Z","avatar_url":"https://github.com/goldmansachs.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JrPip - Java Remote Proxy Invocation Provider\n\nJrPip provides remote method invocation using the Java binary serialization protocol. \nThe payload is streamed as the objects are being serialized, which significantly improves\nspeed and reduces memory consumption for large payloads. It can easily handle multi-GB \npayloads without multi-GB memory buffers.\n\n## Features\n* Streamed serialized payloads. Constant memory usage for all payloads.\n* Execute once semantics: temporary network issues are handled transparently without re-executing the remote code.\n* No RemoteException. A runtime exception is thrown if the destination becomes completely unreachable during a call.\n* Implemented over HTTP in any servlet container.\n\t* Can be encrypted using HTTPS.\n\t* Can use HTTP authentication (cookie or header).\n\t* Proxy/loadbalancing supportable.\n* Has a built in socket transport in addition to HTTP\n* JrPip is safer than RMI because it never creates classes from the binary payload.\n* JrPip does not in any way interfere with garbage collection.\n* Binary logging.\n* Ability to route to a local implementation (when configured).\n* Server side method interceptor.\n\n### Timeout specification\nThe default timeout is zero (infinite), which is appropriate for heavy workloads\nwithin closely related endpoints (e.g. same application).\n\nThe timeout can be specified in 4 ways, with the following precedence:\n- Highest precedence: `System.properties`. A timeout can be specified with the full class\nname and method signature. Example:\n```java\n    System.setProperty(\"jrpip.timeout.com.gs.jrpip.Echo.echoAndSleep_java.lang.String_long\", \"1000\");\n```\n- `@Timeout` annotation on the method in the interface. Example:\n```java\n    @Timeout(timeoutMillis = 10000)\n    public int someMethod(String val);\n```\n- `@Timeout` annotation on the interface class. Example:\n```java\n@Timeout(timeoutMillis = 5000)\npublic interface ExampleService\n```\n- Timeout value used in the call to `factory.create`\n\n### Binary Logging:\nThe following System properties can be used to configure binary logging.\n\n* `jrpip.enableBinaryLogs`: boolean. Default: `false`. Enables binary logging.\n* `jrpip.binaryLogsDirectory`: string. Default: `jrpipBinaryLogs`. Directory where binary logs are stored.\n\nThe produced log file can be inspected with the utility ListJrpipRequest. Run that command to see the options.\n\n## Usage with a servlet container\n1. Create an interface for the service.\n2. Create an implementation of that interface. All objects in the implementation method signatures must be serializable and present\non the classpaths of both the client and server.\n3. Deploy JrpipServlet in a servlet container with a configuration that binds the interface to the implementation.\n4. On the client, get an instance of the interface from the FastServletProxyFactory and call methods on it.\n\n### Example configuration:\nOne or more interface/implementation pairs can be configured for a given servlet.\n\n#### Typical xml based configuration:\n```xml\n    \u003cservlet\u003e\n        \u003cservlet-name\u003eJrpipServlet\u003c/servlet-name\u003e\n        \u003cservlet-class\u003ecom.gs.jrpip.server.JrpipServlet\u003c/servlet-class\u003e\n        \u003cinit-param\u003e\n            \u003cparam-name\u003eserviceInterface.Example\u003c/param-name\u003e\n            \u003cparam-value\u003ecom.example.ExampleService\u003c/param-value\u003e \u003c!-- this is the interface --\u003e\n        \u003c/init-param\u003e\n        \u003cinit-param\u003e\n            \u003cparam-name\u003eserviceClass.Example\u003c/param-name\u003e\n            \u003cparam-value\u003ecom.example.ExampleServiceImpl\u003c/param-value\u003e \u003c!-- this is the implementation --\u003e\n        \u003c/init-param\u003e\n        \u003cinit-param\u003e\n            \u003cparam-name\u003eserviceInterface.FooService\u003c/param-name\u003e\n            \u003cparam-value\u003ecom.example.foo.FooService\u003c/param-value\u003e\n        \u003c/init-param\u003e\n        \u003cinit-param\u003e\n            \u003cparam-name\u003eserviceClass.FooService\u003c/param-name\u003e\n            \u003cparam-value\u003ecom.example.foo.FooServiceImpl\u003c/param-value\u003e\n        \u003c/init-param\u003e\n    \u003c/servlet\u003e\n```\n\n#### Typical Jetty configuration:\n```java\n        Server server = new Server(9001);\n\n        ServletHandler handler = new ServletHandler();\n        server.setHandler(handler);\n\n        ServletHolder holder = handler.addServletWithMapping(JrpipServlet.class, \"/JrpipServlet\");\n        holder.setInitParameter(\"serviceInterface.Echo\", \"com.gs.jrpip.Echo\"); // this is the interface\n        holder.setInitParameter(\"serviceClass.Echo\", \"com.gs.jrpip.EchoImpl\"); // this is the implementation\n\n        server.start();\n```\n\nAnd older versions of Jetty:\n```java\n        HttpServer server = new HttpServer();\n\n        HttpContext context = new HttpContext();\n        context.setContextPath(\"/\");\n\n        ServletHandler servletHandler = new ServletHandler();\n        context.addHandler(servletHandler);\n\n        ServletHolder holder = servletHandler.addServlet(\"JrpipServlet\", \"/JrpipServlet\", \"com.gs.jrpip.server.JrpipServlet\");\n        holder.put(\"serviceInterface.Echo\", \"com.gs.jrpip.Echo\"); // this is the interface\n        holder.put(\"serviceClass.Echo\", \"com.gs.jrpip.EchoImpl\"); // this is the implementation\n\n        server.addContext(context);\n        server.start();\n```\n\n### Client side usage:\n```java\n        FastServletProxyFactory fspf = new FastServletProxyFactory();\n        Echo echo = fspf.create(Echo.class, this.getJrpipUrl()); // HTTP url, e.g. http://example.com:8080/JrpipServlet\n        echo.someMethod();\n```\n### Using other features:\n* Overloaded `create` methods on `FastServletProxyFactory` can be used to specify timeouts. By default, the remote\nmethod can run as long as it needs to.\n* For HTTP basic AUTH, use `new FastServletProxyFactory(USER, PASSWORD)`.\n* For HTTP cookie handling, use `new FastServletProxyFactory(tokenArray, path, domain)`.\n* To register a local implementation, call `JrpipServiceRegistry.getInstance().addServiceForWebApp` or \n`JrpipServiceRegistry.getInstance().addServiceForUrl`. \n\n### Connection manager configuration:\nThe following System properties can be used to configure JrPip (from the java command line):\n\n* `fastServletProxyFactory.maxConnectionsPerHost`: integer. Default: 10. Maximum number of HTTP connections per host.\n* `fastServletProxyFactory.maxTotalConnections`: integer. Default `10 * maxConnectionsPerHost`. Maxium number of total connections.\n\nThe following static methods on `FastServletProxyFactory` can be used for similar configuration:\n* `setMaxConnectionsPerHost`\n* `setMaxTotalConnections`\n\nFor sticky sessions (typically used with cookies and a loadbalancer), use `SessionAwareFastServletProxyFactory`.\n\n### Method interceptor:\nTo configure a method interceptor, add a parameter to the servlet configuration:\n```xml\n        \u003cinit-param\u003e\n            \u003cparam-name\u003emethodInterceptor\u003c/param-name\u003e\n            \u003cparam-value\u003ecom.example.foo.FooMethodInterceptor\u003c/param-value\u003e\n        \u003c/init-param\u003e\n```\nor for Jetty:\n```java\n\tholder.put(\"methodInterceptor\", TestMethodInterceptor.class.getName())\n```\n\nThe method interceptor must implement `com.gs.jrpip.server.MethodInterceptor` and have a no-arg constructor.\nSee the javadoc in `com.gs.jrpip.server.MethodInterceptor` for call semantics.\n\n### VM Bound configuration:\nIn some cases, usually when the service implementation is stateful in some way, it is desirable to \ndisallow the client from connecting to a new instance of the server. To configure such a service, \nin the servlet configuration, replace \"serviceClass\" with \"vmBoundServiceClass\". \n\nExample:\n```xml\n    \u003cservlet\u003e\n        \u003cservlet-name\u003eJrpipServlet\u003c/servlet-name\u003e\n        \u003cservlet-class\u003ecom.gs.jrpip.server.JrpipServlet\u003c/servlet-class\u003e\n        \u003cinit-param\u003e\n            \u003cparam-name\u003eserviceInterface.Example\u003c/param-name\u003e\n            \u003cparam-value\u003ecom.example.ExampleService\u003c/param-value\u003e\n        \u003c/init-param\u003e\n        \u003cinit-param\u003e\n            \u003cparam-name\u003evmBoundServiceClass.Example\u003c/param-name\u003e \u003c!-- this implementation is VM bound --\u003e\n            \u003cparam-value\u003ecom.example.ExampleServiceImpl\u003c/param-value\u003e\n        \u003c/init-param\u003e\n    \u003c/servlet\u003e\n```\n\n## Usage with socket transport\n1. Create an interface for the service.\n2. Create an implementation of that interface. All objects in the implementation method signatures must be serializable and present\non the classpaths of both the client and server.\n3. Deploy a `SocketServer` configured via `SocketServerConfig`.\n4. On the client, get an instance of the interface from the `MtProxyFactory` and call methods on it.\n\n### Example configuration:\nOne or more interface/implementation pairs can be configured for a given server.\nSee the javadoc for `SocketServerConfig` for more options.\n\n```java\n    SocketServerConfig config = new SocketServerConfig(9001);\n    config.addServiceConfig(ExampleService.class, ExampleServiceImpl.class);\n    config.addServiceConfig(AnotherService.class, AnotherServiceImpl.class);\n    SocketServer server = new SocketServer(config);\n    server.start();\n```\n\n### Client side usage:\n```java\n    SocketMessageTransport transport = new SocketMessageTransport();\n    MtProxyFactory factory = new MtProxyFactory(transport);\n    ExampleService example = factory.create(ExampleService.class,\n            \"jpfs://localhost:9001\"); // URL for socket server is always jpfs://\u003cserver\u003e:\u003cport\u003e\n```\n\n### Authentication\nThe socket server can be secured with a username/token pair (or pairs). The authentication\nsends the username and a hashed challenge (nonce), so the token is never sent over the wire.\nA socket is only authenticated once, right after connection.\nOn the server side:\n```java\n    config.addCredentials(\"fred\", \"lkjhhjas56786349873dliuonkje\");\n```\nand on the client:\n```java\n    SocketMessageTransport transport = new SocketMessageTransport(\"fred\", \"lkjhhjas56786349873dliuonkje\");\n```\n\n## Encryption\nEncryption (AES-128/CBC) can be enabled when constructing the client side transport.\n```java\n    SocketMessageTransport transport = new SocketMessageTransport(\"fred\", \"lkjhhjas56786349873dliuonkje\", true);\n```\nEncryption requires authentication, as it derives a per-session key from\nthe user token and per-session nonce.\n\n### Method interceptor:\nSee the javadoc for `SocketServerConfig` and `MethodInterceptor`\n\n### Event Listener\nSee the javadoc for `SocketServerConfig` and `JrpipEventListener`\n\n### VM Bound configuration:\nIn some cases, usually when the service implementation is stateful in some way, it is desirable to\ndisallow the client from connecting to a new instance of the server. To configure such a service,\nadd a vmbound (boolean) to the service configuration call:\n\n```java\n    config.addServiceConfig(ExampleService.class, ExampleServiceImpl.class, true);\n```\n\n### Compression\nThe socket based transport can additionally turn off compression via annotations.\n`@Compression(compress = false)` can be specified at the interface class level, or\nat the method level, with the method level overriding the class level.\nThe LZ4 compression used in JrPip is very fast/light and generally there is no\nbenefit in changing it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoldmansachs%2Fjrpip","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoldmansachs%2Fjrpip","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoldmansachs%2Fjrpip/lists"}