From 0234936c0febfdc2f9a6d465626596f598f9bc59 Mon Sep 17 00:00:00 2001 From: Hasini Samarathunga Date: Mon, 30 Sep 2024 18:20:23 +0530 Subject: [PATCH] Remove HttpInvokerServiceExporter class and uses from spring --- build.gradle | 7 + gradle/spring-module.gradle | 21 + .../remoting/rmi/RmiServiceExporter.java | 1 - .../remoting/support/RemoteInvocation.java | 1 - .../support/RemoteInvocationExecutor.java | 4 +- .../caucho/HessianServiceExporter.java | 1 - .../HttpInvokerClientInterceptor.java | 1 - .../HttpInvokerProxyFactoryBean.java | 1 - .../HttpInvokerServiceExporter.java | 215 --------- .../SimpleHttpInvokerServiceExporter.java | 1 - .../web/HttpRequestHandler.java | 4 +- .../support/HttpRequestHandlerServlet.java | 3 +- .../httpinvoker/HttpInvokerTests.java | 453 ------------------ src/docs/asciidoc/integration.adoc | 121 ----- 14 files changed, 31 insertions(+), 803 deletions(-) delete mode 100644 spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java delete mode 100644 spring-web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java diff --git a/build.gradle b/build.gradle index a0d87c6514ed..1d244c529ce2 100644 --- a/build.gradle +++ b/build.gradle @@ -441,5 +441,12 @@ configure(rootProject) { } } } +} + +tasks.named('build').configure { + finalizedBy('publishToMavenLocal') +} +tasks.named('publish').configure { + dependsOn('build') } diff --git a/gradle/spring-module.gradle b/gradle/spring-module.gradle index 7628127cb5d4..8f1a17aa5084 100644 --- a/gradle/spring-module.gradle +++ b/gradle/spring-module.gradle @@ -95,8 +95,29 @@ publishing { artifact javadocJar } } + + repositories { + maven { + name 'nexus' + url = System.getenv("NEXUS_REPO_URL") ? System.getenv("NEXUS_REPO_URL") : + "https://maven.wso2.org/nexus/content/repositories/releases" + credentials { + username rootProject.hasProperty("nexus_username") ? nexus_username : System.getenv("NEXUS_USERNAME") + password rootProject.hasProperty("nexus_password") ? nexus_password : System.getenv("NEXUS_PASSWORD") + } + allowInsecureProtocol = false + } + } } // Disable publication of test fixture artifacts. components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() } components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } + +tasks.named('build').configure { + finalizedBy('publishToMavenLocal') +} + +tasks.named('publish').configure { + dependsOn('build') +} diff --git a/spring-context/src/main/java/org/springframework/remoting/rmi/RmiServiceExporter.java b/spring-context/src/main/java/org/springframework/remoting/rmi/RmiServiceExporter.java index 9ee3a6c112bf..741d23d97b6f 100644 --- a/spring-context/src/main/java/org/springframework/remoting/rmi/RmiServiceExporter.java +++ b/spring-context/src/main/java/org/springframework/remoting/rmi/RmiServiceExporter.java @@ -64,7 +64,6 @@ * @see java.rmi.Remote * @see java.rmi.RemoteException * @see org.springframework.remoting.caucho.HessianServiceExporter - * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter * @deprecated as of 5.3 (phasing out serialization-based remoting) */ @Deprecated diff --git a/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocation.java b/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocation.java index fec43c6bb209..371c4e2918c0 100644 --- a/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocation.java +++ b/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocation.java @@ -45,7 +45,6 @@ * @see org.springframework.remoting.rmi.RmiProxyFactoryBean * @see org.springframework.remoting.rmi.RmiServiceExporter * @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean - * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter */ public class RemoteInvocation implements Serializable { diff --git a/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocationExecutor.java b/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocationExecutor.java index 02d456b3a4be..89ab71d5a84d 100644 --- a/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocationExecutor.java +++ b/spring-context/src/main/java/org/springframework/remoting/support/RemoteInvocationExecutor.java @@ -21,14 +21,12 @@ /** * Strategy interface for executing a {@link RemoteInvocation} on a target object. * - *

Used by {@link org.springframework.remoting.rmi.RmiServiceExporter} (for RMI invokers) - * and by {@link org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter}. + *

Used by {@link org.springframework.remoting.rmi.RmiServiceExporter} (for RMI invokers). * * @author Juergen Hoeller * @since 1.1 * @see DefaultRemoteInvocationFactory * @see org.springframework.remoting.rmi.RmiServiceExporter#setRemoteInvocationExecutor - * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#setRemoteInvocationExecutor */ public interface RemoteInvocationExecutor { diff --git a/spring-web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java b/spring-web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java index f25139ccf263..97e9a1513ea4 100644 --- a/spring-web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java +++ b/spring-web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java @@ -42,7 +42,6 @@ * @since 13.05.2003 * @see HessianClientInterceptor * @see HessianProxyFactoryBean - * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter * @see org.springframework.remoting.rmi.RmiServiceExporter * @deprecated as of 5.3 (phasing out serialization-based remoting) */ diff --git a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerClientInterceptor.java b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerClientInterceptor.java index 361101475e34..0ae0c5f644fe 100644 --- a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerClientInterceptor.java +++ b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerClientInterceptor.java @@ -66,7 +66,6 @@ * @see #setCodebaseUrl * @see #setRemoteInvocationFactory * @see #setHttpInvokerRequestExecutor - * @see HttpInvokerServiceExporter * @see HttpInvokerProxyFactoryBean * @see java.rmi.server.RMIClassLoader * @deprecated as of 5.3 (phasing out serialization-based remoting) diff --git a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java index ae483e45ff80..0b2d26df05e7 100644 --- a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java @@ -50,7 +50,6 @@ * @see #setServiceUrl * @see #setCodebaseUrl * @see HttpInvokerClientInterceptor - * @see HttpInvokerServiceExporter * @see org.springframework.remoting.rmi.RmiProxyFactoryBean * @see org.springframework.remoting.caucho.HessianProxyFactoryBean * @deprecated as of 5.3 (phasing out serialization-based remoting) diff --git a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java deleted file mode 100644 index dc1a0ac746bf..000000000000 --- a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.remoting.httpinvoker; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.remoting.support.RemoteInvocation; -import org.springframework.remoting.support.RemoteInvocationResult; -import org.springframework.web.HttpRequestHandler; -import org.springframework.web.util.NestedServletException; - -/** - * Servlet-API-based HTTP request handler that exports the specified service bean - * as HTTP invoker service endpoint, accessible via an HTTP invoker proxy. - * - *

Deserializes remote invocation objects and serializes remote invocation - * result objects. Uses Java serialization just like RMI, but provides the - * same ease of setup as Caucho's HTTP-based Hessian protocol. - * - *

HTTP invoker is the recommended protocol for Java-to-Java remoting. - * It is more powerful and more extensible than Hessian, at the expense of - * being tied to Java. Nevertheless, it is as easy to set up as Hessian, - * which is its main advantage compared to RMI. - * - *

WARNING: Be aware of vulnerabilities due to unsafe Java deserialization: - * Manipulated input streams could lead to unwanted code execution on the server - * during the deserialization step. As a consequence, do not expose HTTP invoker - * endpoints to untrusted clients but rather just between your own services. - * In general, we strongly recommend any other message format (e.g. JSON) instead. - * - * @author Juergen Hoeller - * @since 1.1 - * @see HttpInvokerClientInterceptor - * @see HttpInvokerProxyFactoryBean - * @see org.springframework.remoting.rmi.RmiServiceExporter - * @see org.springframework.remoting.caucho.HessianServiceExporter - * @deprecated as of 5.3 (phasing out serialization-based remoting) - */ -@Deprecated -public class HttpInvokerServiceExporter extends org.springframework.remoting.rmi.RemoteInvocationSerializingExporter implements HttpRequestHandler { - - /** - * Reads a remote invocation from the request, executes it, - * and writes the remote invocation result to the response. - * @see #readRemoteInvocation(HttpServletRequest) - * @see #invokeAndCreateResult(org.springframework.remoting.support.RemoteInvocation, Object) - * @see #writeRemoteInvocationResult(HttpServletRequest, HttpServletResponse, RemoteInvocationResult) - */ - @Override - public void handleRequest(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - try { - RemoteInvocation invocation = readRemoteInvocation(request); - RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy()); - writeRemoteInvocationResult(request, response, result); - } - catch (ClassNotFoundException ex) { - throw new NestedServletException("Class not found during deserialization", ex); - } - } - - /** - * Read a RemoteInvocation from the given HTTP request. - *

Delegates to {@link #readRemoteInvocation(HttpServletRequest, InputStream)} with - * the {@link HttpServletRequest#getInputStream() servlet request's input stream}. - * @param request current HTTP request - * @return the RemoteInvocation object - * @throws IOException in case of I/O failure - * @throws ClassNotFoundException if thrown by deserialization - */ - protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) - throws IOException, ClassNotFoundException { - - return readRemoteInvocation(request, request.getInputStream()); - } - - /** - * Deserialize a RemoteInvocation object from the given InputStream. - *

Gives {@link #decorateInputStream} a chance to decorate the stream - * first (for example, for custom encryption or compression). Creates a - * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream} - * and calls {@link #doReadRemoteInvocation} to actually read the object. - *

Can be overridden for custom serialization of the invocation. - * @param request current HTTP request - * @param is the InputStream to read from - * @return the RemoteInvocation object - * @throws IOException in case of I/O failure - * @throws ClassNotFoundException if thrown during deserialization - */ - protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is) - throws IOException, ClassNotFoundException { - - try (ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, is))) { - return doReadRemoteInvocation(ois); - } - } - - /** - * Return the InputStream to use for reading remote invocations, - * potentially decorating the given original InputStream. - *

The default implementation returns the given stream as-is. - * Can be overridden, for example, for custom encryption or compression. - * @param request current HTTP request - * @param is the original InputStream - * @return the potentially decorated InputStream - * @throws IOException in case of I/O failure - */ - protected InputStream decorateInputStream(HttpServletRequest request, InputStream is) throws IOException { - return is; - } - - /** - * Write the given RemoteInvocationResult to the given HTTP response. - * @param request current HTTP request - * @param response current HTTP response - * @param result the RemoteInvocationResult object - * @throws IOException in case of I/O failure - */ - protected void writeRemoteInvocationResult( - HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result) - throws IOException { - - response.setContentType(getContentType()); - writeRemoteInvocationResult(request, response, result, response.getOutputStream()); - } - - /** - * Serialize the given RemoteInvocation to the given OutputStream. - *

The default implementation gives {@link #decorateOutputStream} a chance - * to decorate the stream first (for example, for custom encryption or compression). - * Creates an {@link java.io.ObjectOutputStream} for the final stream and calls - * {@link #doWriteRemoteInvocationResult} to actually write the object. - *

Can be overridden for custom serialization of the invocation. - * @param request current HTTP request - * @param response current HTTP response - * @param result the RemoteInvocationResult object - * @param os the OutputStream to write to - * @throws IOException in case of I/O failure - * @see #decorateOutputStream - * @see #doWriteRemoteInvocationResult - */ - protected void writeRemoteInvocationResult( - HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result, OutputStream os) - throws IOException { - - try (ObjectOutputStream oos = - createObjectOutputStream(new FlushGuardedOutputStream(decorateOutputStream(request, response, os)))) { - doWriteRemoteInvocationResult(result, oos); - } - } - - /** - * Return the OutputStream to use for writing remote invocation results, - * potentially decorating the given original OutputStream. - *

The default implementation returns the given stream as-is. - * Can be overridden, for example, for custom encryption or compression. - * @param request current HTTP request - * @param response current HTTP response - * @param os the original OutputStream - * @return the potentially decorated OutputStream - * @throws IOException in case of I/O failure - */ - protected OutputStream decorateOutputStream( - HttpServletRequest request, HttpServletResponse response, OutputStream os) throws IOException { - - return os; - } - - - /** - * Decorate an {@code OutputStream} to guard against {@code flush()} calls, - * which are turned into no-ops. - *

Because {@link ObjectOutputStream#close()} will in fact flush/drain - * the underlying stream twice, this {@link FilterOutputStream} will - * guard against individual flush calls. Multiple flush calls can lead - * to performance issues, since writes aren't gathered as they should be. - * @see SPR-14040 - */ - private static class FlushGuardedOutputStream extends FilterOutputStream { - - public FlushGuardedOutputStream(OutputStream out) { - super(out); - } - - @Override - public void flush() throws IOException { - // Do nothing on flush - } - } - -} diff --git a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java index 5c8e431fbaf2..b4bc68011367 100644 --- a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java +++ b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java @@ -53,7 +53,6 @@ * @since 2.5.1 * @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor * @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean - * @deprecated as of Spring Framework 5.1, in favor of {@link HttpInvokerServiceExporter} */ @Deprecated @org.springframework.lang.UsesSunHttpServer diff --git a/spring-web/src/main/java/org/springframework/web/HttpRequestHandler.java b/spring-web/src/main/java/org/springframework/web/HttpRequestHandler.java index 187d9dd8c544..5552cadf6634 100644 --- a/spring-web/src/main/java/org/springframework/web/HttpRequestHandler.java +++ b/spring-web/src/main/java/org/springframework/web/HttpRequestHandler.java @@ -50,8 +50,7 @@ * DispatcherServlet, indicating that there will never be a view to render. * *

As of Spring 2.0, Spring's HTTP-based remote exporters, such as - * {@link org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter} - * and {@link org.springframework.remoting.caucho.HessianServiceExporter}, + * {@link org.springframework.remoting.caucho.HessianServiceExporter}, * implement this interface rather than the more extensive Controller interface, * for minimal dependencies on Spring-specific web infrastructure. * @@ -71,7 +70,6 @@ * @see org.springframework.web.servlet.mvc.Controller * @see org.springframework.web.servlet.mvc.LastModified * @see org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter - * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter * @see org.springframework.remoting.caucho.HessianServiceExporter */ @FunctionalInterface diff --git a/spring-web/src/main/java/org/springframework/web/context/support/HttpRequestHandlerServlet.java b/spring-web/src/main/java/org/springframework/web/context/support/HttpRequestHandlerServlet.java index 97be4db90667..8a18fc0e5e2e 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/HttpRequestHandlerServlet.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/HttpRequestHandlerServlet.java @@ -37,8 +37,7 @@ * HttpRequestHandlerServlet servlet-name as defined in {@code web.xml}. * *

This can for example be used to expose a single Spring remote exporter, - * such as {@link org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter} - * or {@link org.springframework.remoting.caucho.HessianServiceExporter}, + * such as {@link org.springframework.remoting.caucho.HessianServiceExporter}, * per HttpRequestHandlerServlet definition. This is a minimal alternative * to defining remote exporters as beans in a DispatcherServlet context * (with advanced mapping and interception facilities being available there). diff --git a/spring-web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java b/spring-web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java deleted file mode 100644 index 8a2c697b69d8..000000000000 --- a/spring-web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright 2002-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.remoting.httpinvoker; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.aopalliance.intercept.MethodInvocation; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.testfixture.beans.ITestBean; -import org.springframework.beans.testfixture.beans.TestBean; -import org.springframework.remoting.RemoteAccessException; -import org.springframework.remoting.support.DefaultRemoteInvocationExecutor; -import org.springframework.remoting.support.RemoteInvocation; -import org.springframework.remoting.support.RemoteInvocationResult; -import org.springframework.web.testfixture.servlet.MockHttpServletRequest; -import org.springframework.web.testfixture.servlet.MockHttpServletResponse; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; - -/** - * @author Juergen Hoeller - * @since 09.08.2004 - */ -@SuppressWarnings("deprecation") -class HttpInvokerTests { - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporter() { - doTestHttpInvokerProxyFactoryBeanAndServiceExporter(false); - } - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporterWithExplicitClassLoader() { - doTestHttpInvokerProxyFactoryBeanAndServiceExporter(true); - } - - private void doTestHttpInvokerProxyFactoryBeanAndServiceExporter(boolean explicitClassLoader) { - TestBean target = new TestBean("myname", 99); - - final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); - exporter.setServiceInterface(ITestBean.class); - exporter.setService(target); - exporter.afterPropertiesSet(); - - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl("https://myurl"); - - pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { - @Override - protected RemoteInvocationResult doExecuteRequest( - HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { - assertThat(config.getServiceUrl()).isEqualTo("https://myurl"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setContent(baos.toByteArray()); - exporter.handleRequest(request, response); - return readRemoteInvocationResult( - new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); - } - }); - if (explicitClassLoader) { - ((BeanClassLoaderAware) pfb.getHttpInvokerRequestExecutor()).setBeanClassLoader(getClass().getClassLoader()); - } - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - assertThat(proxy.getName()).isEqualTo("myname"); - assertThat(proxy.getAge()).isEqualTo(99); - proxy.setAge(50); - assertThat(proxy.getAge()).isEqualTo(50); - proxy.setStringArray(new String[] {"str1", "str2"}); - assertThat(Arrays.equals(new String[] {"str1", "str2"}, proxy.getStringArray())).isTrue(); - proxy.setSomeIntegerArray(new Integer[] {1, 2, 3}); - assertThat(Arrays.equals(new Integer[] {1, 2, 3}, proxy.getSomeIntegerArray())).isTrue(); - proxy.setNestedIntegerArray(new Integer[][] {{1, 2, 3}, {4, 5, 6}}); - Integer[][] integerArray = proxy.getNestedIntegerArray(); - assertThat(Arrays.equals(new Integer[] {1, 2, 3}, integerArray[0])).isTrue(); - assertThat(Arrays.equals(new Integer[] {4, 5, 6}, integerArray[1])).isTrue(); - proxy.setSomeIntArray(new int[] {1, 2, 3}); - assertThat(Arrays.equals(new int[] {1, 2, 3}, proxy.getSomeIntArray())).isTrue(); - proxy.setNestedIntArray(new int[][] {{1, 2, 3}, {4, 5, 6}}); - int[][] intArray = proxy.getNestedIntArray(); - assertThat(Arrays.equals(new int[] {1, 2, 3}, intArray[0])).isTrue(); - assertThat(Arrays.equals(new int[] {4, 5, 6}, intArray[1])).isTrue(); - - assertThatIllegalStateException().isThrownBy(() -> - proxy.exceptional(new IllegalStateException())); - assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() -> - proxy.exceptional(new IllegalAccessException())); - } - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporterWithIOException() throws Exception { - TestBean target = new TestBean("myname", 99); - - final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); - exporter.setServiceInterface(ITestBean.class); - exporter.setService(target); - exporter.afterPropertiesSet(); - - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl("https://myurl"); - - pfb.setHttpInvokerRequestExecutor((config, invocation) -> { throw new IOException("argh"); }); - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - assertThatExceptionOfType(RemoteAccessException.class) - .isThrownBy(() -> proxy.setAge(50)) - .withCauseInstanceOf(IOException.class); - } - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporterWithGzipCompression() { - TestBean target = new TestBean("myname", 99); - - final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter() { - @Override - protected InputStream decorateInputStream(HttpServletRequest request, InputStream is) throws IOException { - if ("gzip".equals(request.getHeader("Compression"))) { - return new GZIPInputStream(is); - } - else { - return is; - } - } - @Override - protected OutputStream decorateOutputStream( - HttpServletRequest request, HttpServletResponse response, OutputStream os) throws IOException { - if ("gzip".equals(request.getHeader("Compression"))) { - return new GZIPOutputStream(os); - } - else { - return os; - } - } - }; - exporter.setServiceInterface(ITestBean.class); - exporter.setService(target); - exporter.afterPropertiesSet(); - - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl("https://myurl"); - - pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { - @Override - protected RemoteInvocationResult doExecuteRequest( - HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) - throws IOException, ClassNotFoundException { - assertThat(config.getServiceUrl()).isEqualTo("https://myurl"); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Compression", "gzip"); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setContent(baos.toByteArray()); - try { - exporter.handleRequest(request, response); - } - catch (ServletException ex) { - throw new IOException(ex.toString()); - } - return readRemoteInvocationResult( - new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); - } - @Override - protected OutputStream decorateOutputStream(OutputStream os) throws IOException { - return new GZIPOutputStream(os); - } - @Override - protected InputStream decorateInputStream(InputStream is) throws IOException { - return new GZIPInputStream(is); - } - }); - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - assertThat(proxy.getName()).isEqualTo("myname"); - assertThat(proxy.getAge()).isEqualTo(99); - proxy.setAge(50); - assertThat(proxy.getAge()).isEqualTo(50); - - assertThatIllegalStateException().isThrownBy(() -> - proxy.exceptional(new IllegalStateException())); - assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() -> - proxy.exceptional(new IllegalAccessException())); - } - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporterWithWrappedInvocations() { - TestBean target = new TestBean("myname", 99); - - final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter() { - @Override - protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - Object obj = ois.readObject(); - if (!(obj instanceof TestRemoteInvocationWrapper)) { - throw new IOException("Deserialized object needs to be assignable to type [" + - TestRemoteInvocationWrapper.class.getName() + "]: " + obj); - } - return ((TestRemoteInvocationWrapper) obj).remoteInvocation; - } - @Override - protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos) - throws IOException { - oos.writeObject(new TestRemoteInvocationResultWrapper(result)); - } - }; - exporter.setServiceInterface(ITestBean.class); - exporter.setService(target); - exporter.afterPropertiesSet(); - - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl("https://myurl"); - - pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { - @Override - protected RemoteInvocationResult doExecuteRequest( - HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { - assertThat(config.getServiceUrl()).isEqualTo("https://myurl"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setContent(baos.toByteArray()); - exporter.handleRequest(request, response); - return readRemoteInvocationResult( - new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); - } - @Override - protected void doWriteRemoteInvocation(RemoteInvocation invocation, ObjectOutputStream oos) throws IOException { - oos.writeObject(new TestRemoteInvocationWrapper(invocation)); - } - @Override - protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois) - throws IOException, ClassNotFoundException { - Object obj = ois.readObject(); - if (!(obj instanceof TestRemoteInvocationResultWrapper)) { - throw new IOException("Deserialized object needs to be assignable to type [" - + TestRemoteInvocationResultWrapper.class.getName() + "]: " + obj); - } - return ((TestRemoteInvocationResultWrapper) obj).remoteInvocationResult; - } - }); - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - assertThat(proxy.getName()).isEqualTo("myname"); - assertThat(proxy.getAge()).isEqualTo(99); - proxy.setAge(50); - assertThat(proxy.getAge()).isEqualTo(50); - - assertThatIllegalStateException().isThrownBy(() -> - proxy.exceptional(new IllegalStateException())); - assertThatExceptionOfType(IllegalAccessException.class).isThrownBy(() -> - proxy.exceptional(new IllegalAccessException())); - } - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporterWithInvocationAttributes() { - TestBean target = new TestBean("myname", 99); - - final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); - exporter.setServiceInterface(ITestBean.class); - exporter.setService(target); - exporter.setRemoteInvocationExecutor(new DefaultRemoteInvocationExecutor() { - @Override - public Object invoke(RemoteInvocation invocation, Object targetObject) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - assertThat(invocation.getAttributes()).isNotNull(); - assertThat(invocation.getAttributes().size()).isEqualTo(1); - assertThat(invocation.getAttributes().get("myKey")).isEqualTo("myValue"); - assertThat(invocation.getAttribute("myKey")).isEqualTo("myValue"); - return super.invoke(invocation, targetObject); - } - }); - exporter.afterPropertiesSet(); - - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl("https://myurl"); - pfb.setRemoteInvocationFactory(methodInvocation -> { - RemoteInvocation invocation = new RemoteInvocation(methodInvocation); - invocation.addAttribute("myKey", "myValue"); - assertThatIllegalStateException().isThrownBy(() -> - invocation.addAttribute("myKey", "myValue")); - assertThat(invocation.getAttributes()).isNotNull(); - assertThat(invocation.getAttributes().size()).isEqualTo(1); - assertThat(invocation.getAttributes().get("myKey")).isEqualTo("myValue"); - assertThat(invocation.getAttribute("myKey")).isEqualTo("myValue"); - return invocation; - }); - - pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { - @Override - protected RemoteInvocationResult doExecuteRequest( - HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { - assertThat(config.getServiceUrl()).isEqualTo("https://myurl"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setContent(baos.toByteArray()); - exporter.handleRequest(request, response); - return readRemoteInvocationResult( - new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); - } - }); - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - assertThat(proxy.getName()).isEqualTo("myname"); - assertThat(proxy.getAge()).isEqualTo(99); - } - - @Test - void httpInvokerProxyFactoryBeanAndServiceExporterWithCustomInvocationObject() { - TestBean target = new TestBean("myname", 99); - - final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); - exporter.setServiceInterface(ITestBean.class); - exporter.setService(target); - exporter.setRemoteInvocationExecutor(new DefaultRemoteInvocationExecutor() { - @Override - public Object invoke(RemoteInvocation invocation, Object targetObject) - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { - boolean condition = invocation instanceof TestRemoteInvocation; - assertThat(condition).isTrue(); - assertThat(invocation.getAttributes()).isNull(); - assertThat(invocation.getAttribute("myKey")).isNull(); - return super.invoke(invocation, targetObject); - } - }); - exporter.afterPropertiesSet(); - - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl("https://myurl"); - pfb.setRemoteInvocationFactory(methodInvocation -> { - RemoteInvocation invocation = new TestRemoteInvocation(methodInvocation); - assertThat(invocation.getAttributes()).isNull(); - assertThat(invocation.getAttribute("myKey")).isNull(); - return invocation; - }); - - pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { - @Override - protected RemoteInvocationResult doExecuteRequest( - HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { - assertThat(config.getServiceUrl()).isEqualTo("https://myurl"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setContent(baos.toByteArray()); - exporter.handleRequest(request, response); - return readRemoteInvocationResult( - new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); - } - }); - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - assertThat(proxy.getName()).isEqualTo("myname"); - assertThat(proxy.getAge()).isEqualTo(99); - } - - @Test - void httpInvokerWithSpecialLocalMethods() { - String serviceUrl = "https://myurl"; - HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); - pfb.setServiceInterface(ITestBean.class); - pfb.setServiceUrl(serviceUrl); - - pfb.setHttpInvokerRequestExecutor((config, invocation) -> { throw new IOException("argh"); }); - - pfb.afterPropertiesSet(); - ITestBean proxy = (ITestBean) pfb.getObject(); - - // shouldn't go through to remote service - assertThat(proxy.toString().contains("HTTP invoker")).isTrue(); - assertThat(proxy.toString().contains(serviceUrl)).isTrue(); - assertThat(proxy.hashCode()).isEqualTo(proxy.hashCode()); - assertThat(proxy.equals(proxy)).isTrue(); - - // should go through - assertThatExceptionOfType(RemoteAccessException.class) - .isThrownBy(() -> proxy.setAge(50)) - .withCauseInstanceOf(IOException.class); - } - - - @SuppressWarnings("serial") - private static class TestRemoteInvocation extends RemoteInvocation { - - TestRemoteInvocation(MethodInvocation methodInvocation) { - super(methodInvocation); - } - } - - - @SuppressWarnings("serial") - private static class TestRemoteInvocationWrapper implements Serializable { - - private final RemoteInvocation remoteInvocation; - - TestRemoteInvocationWrapper(RemoteInvocation remoteInvocation) { - this.remoteInvocation = remoteInvocation; - } - } - - - @SuppressWarnings("serial") - private static class TestRemoteInvocationResultWrapper implements Serializable { - - private final RemoteInvocationResult remoteInvocationResult; - - TestRemoteInvocationResultWrapper(RemoteInvocationResult remoteInvocationResult) { - this.remoteInvocationResult = remoteInvocationResult; - } - } - -} diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index a5b0dc3e9fe6..23265140971e 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -374,10 +374,6 @@ The following remoting technologies are now deprecated and will not be replaced: `RmiServiceExporter`, Spring supports both traditional RMI (with `java.rmi.Remote` interfaces and `java.rmi.RemoteException`) and transparent remoting through RMI invokers (with any Java interface). -* <>: Spring provides a special remoting strategy that allows - for Java serialization though HTTP, supporting any Java interface (as the RMI - invoker does). The corresponding support classes are `HttpInvokerProxyFactoryBean` - and `HttpInvokerServiceExporter`. * <>: By using Spring's `HessianProxyFactoryBean` and the `HessianServiceExporter`, you can transparently expose your services through the lightweight binary HTTP-based protocol provided by Caucho. @@ -963,123 +959,6 @@ NOTE: The preceding example does not show a flexible kind of security infrastruc more options as far as security is concerned, have a look at the Spring Security project at https://spring.io/projects/spring-security/. - - -[[remoting-httpinvoker]] -=== Spring HTTP Invoker (Deprecated) - -WARNING: As of Spring Framework 5.3, HTTP Invoker support is deprecated and will not be replaced. - -As opposed to Hessian, Spring HTTP invokers are both lightweight protocols that use their own slim -serialization mechanisms and use the standard Java serialization -mechanism to expose services through HTTP. This has a huge advantage if your arguments -and return types are complex types that cannot be serialized by using the serialization -mechanisms Hessian uses (see the next section for more considerations when -you choose a remoting technology). - -Under the hood, Spring uses either the standard facilities provided by the JDK or -Apache `HttpComponents` to perform HTTP calls. If you need more -advanced and easier-to-use functionality, use the latter. See -https://hc.apache.org/httpcomponents-client-ga/[hc.apache.org/httpcomponents-client-ga/] -for more information. - -[CAUTION] -==== -Be aware of vulnerabilities due to unsafe Java deserialization: -Manipulated input streams can lead to unwanted code execution on the server -during the deserialization step. As a consequence, do not expose HTTP invoker -endpoints to untrusted clients. Rather, expose them only between your own services. -In general, we strongly recommend using any other message format (such as JSON) instead. - -If you are concerned about security vulnerabilities due to Java serialization, -consider the general-purpose serialization filter mechanism at the core JVM level, -originally developed for JDK 9 but backported to JDK 8, 7 and 6 in the meantime. See -https://blogs.oracle.com/java-platform-group/entry/incoming_filter_serialization_data_a -and https://openjdk.java.net/jeps/290. -==== - - -[[remoting-httpinvoker-server]] -==== Exposing the Service Object - -Setting up the HTTP invoker infrastructure for a service object closely resembles the -way you would do the same by using Hessian. As Hessian support provides -`HessianServiceExporter`, Spring's HttpInvoker support provides -`org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter`. - -To expose the `AccountService` (mentioned earlier) within a Spring Web MVC -`DispatcherServlet`, the following configuration needs to be in place in the -dispatcher's application context, as the following example shows: - -[source,xml,indent=0,subs="verbatim,quotes"] ----- - - - - ----- - -Such an exporter definition is exposed through the `DispatcherServlet` instance's standard -mapping facilities, as explained in <>. - -Alternatively, you can create an `HttpInvokerServiceExporter` in your root application context -(for example, in `'WEB-INF/applicationContext.xml'`), as the following example shows: - -[source,xml,indent=0,subs="verbatim,quotes"] ----- - - - - ----- - -In addition, you can define a corresponding servlet for this exporter in `web.xml`, with the -servlet name matching the bean name of the target exporter, as the following example shows: - -[source,xml,indent=0,subs="verbatim,quotes"] ----- - - accountExporter - org.springframework.web.context.support.HttpRequestHandlerServlet - - - - accountExporter - /remoting/AccountService - ----- - - -[[remoting-httpinvoker-client]] -==== Linking in the Service at the Client - -Again, linking in the service from the client much resembles the way you would do it -when you use Hessian. By using a proxy, Spring can translate your calls to -HTTP POST requests to the URL that points to the exported service. The following example -shows how to configure this arrangement: - -[source,xml,indent=0,subs="verbatim,quotes"] ----- - - - - ----- - -As mentioned earlier, you can choose what HTTP client you want to use. By default, the -`HttpInvokerProxy` uses the JDK's HTTP functionality, but you can also use the Apache -`HttpComponents` client by setting the `httpInvokerRequestExecutor` property. -The following example shows how to do so: - -[source,xml,indent=0,subs="verbatim,quotes"] ----- - - - ----- - - - [[remoting-jms]] === JMS (Deprecated)