Java Servlet API can forward requests to another path within the same server (identical host:port). But, forwarding to a different host:port — like proxy do — is another story.
I've tried to do that with Jersey Client, adapting the ServletRequest — method, headers, mediatype and body — to a Jersey ClientRequest (with a different base uri), making the call, and adapting back the Jersey ClientResponse — method, headers, mediatype and body — to the ServletResponse.
Adapting those manually seems wrong to me.
Isn't there a pure Servlet API solution?
Or an HTTP client capable of adapting requests back and forth when changing the host:port?
HTTP-Proxy-Servlet does exactly what you need.
Quick configuration
pom.xml
<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.7</version>
</dependency>
web.xml
<servlet>
<servlet-name>solr</servlet-name>
<servlet-class>org.mitre.dsmiley.httpproxy.ProxyServlet</servlet-class>
<init-param>
<param-name>targetUri</param-name>
<param-value>http://solrserver:8983/solr</param-value>
</init-param>
<init-param>
<param-name>log</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>solr</servlet-name>
<url-pattern>/solr/*</url-pattern>
</servlet-mapping>
Spring Integration
see also: HTTP-Proxy-Servlet Issue #15
pom.xml
<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.7</version>
</dependency>
ServletWrappingControllerExt.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.ServletWrappingController;
public class ServletWrappingControllerExt extends ServletWrappingController
{
private String pathToStrip;
public void setPathToStrip(String pathToStrip)
{
this.pathToStrip = pathToStrip;
}
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
final HttpServletRequest wrapper = new HttpServletRequestWrapper(request)
{
#Override
public String getPathInfo()
{
//Please note that getPathInfo returns null if DispatcherServlet is configured to track url-pattern "/"
//It should be configured to track url-pattern "/*". Below is a sample DispatcherServlet configuration
/*
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
*/
String path = super.getPathInfo();
if (path.startsWith(pathToStrip))
{
final int length = pathToStrip.length();
path = path.substring(length);
}
return path;
}
#Override
public String getServletPath()
{
return super.getServletPath();
}
};
return super.handleRequestInternal(wrapper, response);
}
}
Beans configuration
<bean id="myServletWrapper" class="ServletWrappingControllerExt">
<property name="pathToStrip" value="/solr"/>
<property name="servletClass" value="org.mitre.dsmiley.httpproxy.ProxyServlet" />
<property name="servletName" value="solr" />
<property name="initParameters">
<props>
<prop key="targetUri">http://solrserver:8983/solr</prop>
<prop key="log">true</prop>
</props>
</property>
</bean>
<bean id="myServletUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/solr/**" value-ref="myServletWrapper" />
</map>
</property>
<property name="order" value="1" />
</bean>
You should use javax.net.HttpURLConnection
Here is the psuedo code for that:
URL url = new URL("http://otherserver:otherport/url");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
// set http method if required
connection.setRequestMethod("POST");
// set request header if required
connection.setRequestProperty("header1", "value1");
// check status code
if(connection.getResponseCode() == 200) {
InputStream is = connection.getInputStream();
//transfer is to the required output stream
} else {
//write error
}
As far as I understand You need to send requests from Servlet and get response from other server into yours, may be you need a HTTP Client (Overview) for that.
This question might also help you.
Related
Please note that I have Existing project in struts 1.x and with the following steps I am trying to integrate Spring MVC to it.
I have a maven project which on deployment is able to read servlet and load respective Controller class from jar file on server start-up and gives following info about controller class:
[org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping] Rejected bean name 'SSOController': no URL paths identified
Looks like it is not loading URLS hence I am not able to make any restful web service call on it.
my web.xml has following entry :
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
Dispatcher Servlet has following entries:
<context:annotation-config />
<context:component-scan base-package="com.ga.action.controller" />
<mvc:resources location = "/resources/" mapping = "/resources/**" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- JAXB2 marshaller. Automagically turns beans into xml -->
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.ga.action.controller.PEMUtil</value>
</list>
</property>
</bean>
Controller class code follows:
#Controller
#RequestMapping("/hello.do")
public class TestController {
#RequestMapping(method = RequestMethod.GET, value = "/")
public #ResponseBody String printTokenMessage(
#RequestParam("nLiveToken") String message,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
System.out.println(message);
}
}
Use XML configuration instead of #Conteroller annotation for external jar file controllers. Because the #Controller annotation isn't available in the server class loader.
http://forum.spring.io/forum/spring-projects/web/108774-controllers-from-external-jar
Since the exception indicates the use of BeanNameUrlHandlerMapping there should be a bean named /hello.do in your context.
You do use the #RequestMapping annotation so I guess you assumed annotation driven handler mapping would be enabled by default. Which isn't the case. If the ServletDispatcher finds no handler mapping it will create a BeanNameUrlHandlerMapping for you.
If you want to use annotations in your application you should define
<mvc:annotation-driven />
in your context configuration.
I have a very basic setup which I am trying to get working.
web.xml
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/site/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
mvc-dispatcher-servlet.xml
<context:component-scan base-package="com.blabla.controller" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/pages/" p:suffix=".jsp"
p:viewClass="org.springframework.web.servlet.view.JstlView" />
In the controller
#Controller
#RequestMapping(value = "/site")
public class SearchController {
#RequestMapping(value = "welcome", method = RequestMethod.GET)
public String test() {
return "test";
}
This is the problem that I have:
I would like to write /site/* as url-pattern in my web.xml, but when I do that I get
WARNING: No mapping found for HTTP request with URI [/site/welcome] in DispatcherServlet with name 'mvc-dispatcher'
When I write /site/welcome in full, everything works, but I dont want this because I dont want to add every page manually to the web.xml
And when I write "/*" as url-pattern i get the error message:
WARNING: No mapping found for HTTP request with URI [/WEB-INF/pages/test.jsp] in DispatcherServlet with name 'mvc-dispatcher'
which I guess makes sense because the the location of the jsp is included in the pattern.
So how do you do it: how can you be sufficiently vague in your url pattern without the problems I just had?
I have a code in MyController
#RequestMapping("/hello.jsp")
public void handleRequest() {
System.out.println("hello.jsp");
logger.info("Returning hello view");
}
#RequestMapping("/hello2")
public ModelAndView hello2() {
System.out.println("123");
String message = "Hello World, Spring 3.0!";
return new ModelAndView("hello2", "message", message);
}
In dispatcher-servlet.xml I have:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
In the end I have:
~8080/hello2.htm - OK
~8080/hello.htm - NOT OK, aloso I tried: hello.jsp, hello; moved hello.jsp to /WEB-INF/jsp/ and to/WEB-INF/ - no effect
1.hello2() is working well, and redirecting to the hello2.jsp
2.hello() is NOT working, and NOT redirecting
Before putting "viewResolver" into dispatcher-servlet.xml I had opposite behaviour - hello() was working hello2() was not. [but then I had all my jps in WEB-INF folder]
What is the reason?
my web.xml consists this:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
I think this is because you are returning void instead of the view name.
#RequestMapping("/hello")
public String handleRequest() {
System.out.println("hello.jsp");
logger.info("Returning hello view");
return("hello");
}
It was because of '/hello.jsp' - it was trying to find '/hello.jsp.jsp', since I've defined 'viewResolver' with: suffix" value=".jsp".
And before: without 'viewResolver', it was behaving by some default logic, I guess.
While implementing my project, I found that <mvc:resources/> was not working after I set up the controller using #RequestMapping(value = "/**") and #RequestMapping(method=RequestMethod.GET).
It seems that the annotated controller get a higher priority than the SimpleURLHandler.
Is there anybody who can solve this problem? I need that controller and cannot remove it.
Thanks in advance!
Here is how I set up the project and the details about the problem:
Web.xml
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
webmvc-config.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- The controllers are autodetected POJOs labeled with the #Controller annotation. -->
<context:component-scan base-package="com.web.test" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<!-- Turns on support for mapping requests to Spring MVC #Controller methods
Also registers default Formatters and Validators for use across all #Controllers -->
<mvc:annotation-driven conversion-service="applicationConversionService"/>
<bean class="com.web.test.web.ApplicationConversionServiceFactoryBean" id="applicationConversionService"/>
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources -->
<mvc:resources location="classpath:/META-INF/web-resources/, /WEB-INF/views/" mapping="/resources/**" cache-period="0" order="0"/>
<!-- Allows for mapping the DispatcherServlet to "/" by forwarding static resource requests to the container's default Servlet -->
<mvc:default-servlet-handler/>
<!-- selects a static view for rendering without the need for an explicit controller -->
<mvc:view-controller path="/" view-name="index"/>
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver" id="resourceBundleViewResolver" p:basename="META-INF/view/wicket-views" p:order="1"/>
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
<property name="order" value="2"/>
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>
<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/views/layouts/layouts.xml</value>
<value>classpath:/META-INF/view/tiles-views.xml</value>
</list>
</property>
</bean>
... ...
</beans>
ApplicationController.java
#Controller
#RequestMapping(method=RequestMethod.GET)
public class ApplicationController {
#RequestMapping(value = "/**")
public ModelAndView handleRequest(HttpServletRequest request){
ModelAndView mav = new ModelAndView("index");
return mav;
}
}
Running log. (As you can see here, the resource request is processed by annotated controller, not the ResourceHttpRequestHandler).
2011-11-15 17:21:09,821 [http-8080-2] DEBUG org.springframework.web.servlet.DispatcherServlet - Rendering view [org.springframework.web.servlet.view.tiles2.TilesView: name 'index'; URL [index]] in DispatcherServlet with name 'Test'
2011-11-15 17:21:09,821 [http-8080-2] DEBUG org.apache.tiles.impl.BasicTilesContainer - Render request recieved for definition 'index'
2011-11-15 17:21:09,821 [http-8080-4] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'Test' processing GET request for [/Test/resources/javascripts/jQuery/jquery-ui-1.8.16.custom.min.js]
2011-11-15 17:21:09,823 [http-8080-4] DEBUG org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Matching patterns for request [/resources/javascripts/jQuery/jquery-ui-1.8.16.custom.min.js] are [/**/, /**]
2011-11-15 17:21:09,823 [http-8080-4] DEBUG org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - URI Template variables for request [/resources/javascripts/jQuery/jquery-ui-1.8.16.custom.min.js] are {}
2011-11-15 17:21:09,823 [http-8080-4] DEBUG org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/resources/javascripts/jQuery/jquery-ui-1.8.16.custom.min.js] to HandlerExecutionChain with handler [com.web.Test.web.ApplicationController#1a57c9e4] and 4 interceptors
2011-11-15 17:21:09,823 [http-8080-4] DEBUG org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/Test/resources/javascripts/jQuery/jquery-ui-1.8.16.custom.min.js] is: -1
2011-11-15 17:21:09,823 [http-8080-4] DEBUG org.springframework.web.bind.annotation.support.HandlerMethodInvoker - Invoking request handler method: public org.springframework.web.servlet.ModelAndView com.web.Test.web.ApplicationController.handleRequest(javax.servlet.http.HttpServletRequest)
And if I remove the ApplicationController, the static resources can be reached.
2011-11-15 17:48:16,784 [http-8080-2] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'Test' processing GET request for [/Test/resources/styles/application-common.css]
2011-11-15 17:48:16,784 [http-8080-2] DEBUG org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - Matching patterns for request [/resources/styles/application-common.css] are [/resources/**]
2011-11-15 17:48:16,785 [http-8080-2] DEBUG org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - URI Template variables for request [/resources/styles/application-common.css] are {}
2011-11-15 17:48:16,785 [http-8080-2] DEBUG org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - Mapping [/resources/styles/application-common.css] to HandlerExecutionChain with handler [org.springframework.web.servlet.resource.ResourceHttpRequestHandler#6cc5cbab] and 4 interceptors
By using value = "/**" you're saying your ApplicationController will handle absolutely all requested URLs.
Just specify a more restrictive pattern that your static content won't match and it'll work like you want it to; examples:
value = "/dynamic/**"
value = "/**/*.html"
value = "/pages/**"
The choice is up to you but the "RESTful" style is very hot these days, so I'd follow that style guide.
I had the same requirement.
I'm bundling a React app inside my Spring Boot app, but I want all the static resource requests to take highest priority, then fall-back to annotated controllers.
This way all urls that aren't actually static files will resolve to the index/root page of my React app.
For example -
/admin/css/style.css should resolve to a static file.
But a React route should resolve the root index page/view.
/admin/sub/path should resolve to a view.
This works by mapping all the paths in my annotated controller -
#Controller
public class DefaultMvcController {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
#GetMapping({ "", "/"})
public String index() throws IOException {
return "index";
}
#RequestMapping(path = {"/admin", "/admin/**"}, method = RequestMethod.GET)
public String admin() throws IOException {
return "admin/index";
}
}
And changing the order of the static file handler mapping to 1 less than the priority of the annotated controller mapping
#Configuration
#ComponentScan(basePackages = "net.savantly.sprout.controllers")
#RequiredArgsConstructor
public class SproutWebMvcConfigurer extends WebMvcConfigurationSupport {
#Bean
#Override
public HandlerMapping resourceHandlerMapping(UrlPathHelper urlPathHelper, PathMatcher pathMatcher,
ContentNegotiationManager contentNegotiationManager, FormattingConversionService conversionService,
ResourceUrlProvider resourceUrlProvider) {
HandlerMapping mapping = super.resourceHandlerMapping(urlPathHelper, pathMatcher, contentNegotiationManager, conversionService,
resourceUrlProvider);
((AbstractHandlerMapping)mapping).setOrder(-1);
return mapping;
}
}
Full source code here - https://github.com/savantly-net/sprout-platform/tree/development/spring/sprout-spring-boot-starter
I'm a newbie in spring MVC framework. I'm trying to write a simple controller (extends from AbstractController) and want to return a simple view (home.jsp) but seems that the controller's handleRequestInternal() is never called. I'm giving my code below. Any help would be highly appreciated.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="hiperlink is trimmed "
xmlns:xsi="hiperlink is trimmed "
xsi:schemaLocation="hiperlink is trimmed " >
<servlet>
<servlet-name>students</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>students</servlet-name>
<url-pattern>/students/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<jsp-config>
<taglib>
<taglib-uri>/spring</taglib-uri>
<taglib-location>/WEB-INF/spring-form.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>hiperlink is trimmed java.sun.com/jsp/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>hiperlink is trimmed java.sun.com/jsp/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
students-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="hiperlink is trimmed "
xmlns:xsi="hiperlink is trimmed " xmlns:context="hiperlink is trimmed" xmlns:jaxws="hiperlink is trimmed "
xsi:schemaLocation="hiperlink is trimmed ">
<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="myService" class="net.ochinto.student.service.MiscellaneousService"/>
<bean name="/home.htm" class="net.ochinto.student.web.HomePageController">
<property name="myService" ref="myService"/>
</bean>
</beans>
HomePageController.java
public class HomePageController extends AbstractController {
private static final Logger log = LoggerFactory.getLogger(HomePageController.class);
public HomePageController() {
log.debug("HomePageController constructor()");
}
protected ModelAndView handleRequestInternal(
HttpServletRequest request, HttpServletResponse response)
throws Exception {
log.debug("HomePageController handleRequestInternal()");
List<Student> students = myService.getStudentList();
return new ModelAndView("home", "students", students);
}
private MiscellaneousService myService;
public void setMyService(MiscellaneousService myService) {
log.debug("HomePageController setMyService() - start");
this.myService = myService;
log.debug("HomePageController setMyService() - end");
}
}
welcome page is ok but when I write /students/home.htm 404 is returned! Please help.
You say:
welcome page is ok but when I write /students/home.htm 404 is returned! Please help.
Your configuration only has one URL mapping, the one for /home.htm. You need to add additional mappings if you want /students/home.htm to point some where.
Check out the Handler Mapping section of the Spring docs for more info.