I have a class
#Controller
#RequestMapping("/auth")
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(#RequestParam(value="error", required=false) boolean error,
ModelMap model) {
}
}
web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/spring-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
appcontext.xml
<mvc:annotation-driven/>
<context:component-scan base-package="mysrc.src"/>
spring servlet.xml
<context:component-scan base-package="mysrc.src"/>
<mvc:annotation-driven/>
I WAS getting an error like "no mapping found in dispatcher blah blah"
But NOW i can see no exception on console.
I only see 404
http://localhost:9090/app/auth/login
I put breakpoint in getLoginPage method in debug mode but no flow.
Beside suggestions i want to ask how can i be sure abour Spring has scanned an realised my #RequestMapping class? Searching on starting up console maybe?
Usually when you get a 404 with no error message from Spring, it usually means your app is deployed under a different context than you are expecting. For example, your code sample indicates you are deploying your webapp to /, are you sure thats where its being deployed? Are you using tomcat? If so, your webapp should be named ROOT.war. Can you provide more details like where/how you are deploying your webapp and maybe the web.xml as well?
As far as your mappings go, Spring will log which controllers it scanned along with their request path at the INFO logging level. The dispatcher servlet will give always you an error message if it cannot match your request to a controller, so no error message indicates to me your requests are not going through the dispatcher servlet.
Your request mapping is looking right.
You can create an constructor to your controller and put a log statement or a break point there to see if the controller is created by spring.
Note that your component-scan would also initialize the controller twice (once for appcontext.xml and once for servlet.xml). You should add filters to make sure controllers are only created from the servlet.xml:
appcontext.xml (everything except controllers):
<context:component-scan base-package="x.y.z">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
servlet.xml (only controllers):
<context:component-scan base-package="x.y.z" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
And just to be sure: The base-package attribute of component-scan has to be a valid java package and not a path to your src directory (mysrc.src looks a bit like your src folder ;-) )
Related
I have following configuration in the Spring based project -
web.xml -
<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>/api/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Handler mapping from spring-servlet.xml -
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true" />
</bean>
and Spring Controller as -
#RestController
public class TestController {
#PostMapping("/test")
public String test() {
return "hello";
}
}
Now when I send request to
GET http://localhost:8080/test
I get response as hello which is fine but when I send request on following URL's -
GET http://localhost:8080/api/test
GET http://localhost:8080/img/test
then also I get hello response i.e. controller code gets executed for above mentioned wrong URLs.
Is there anything wrong in the configuration or it's expected behaviour?
The mapping of an URL to a handler (a method) in Spring MVC is a two-step process:
first the servlet container must decide whether to forward the request to the DispatcherServlet according to the <servlet-mapping>,
then the DispatcherServlet consults its HandlerMappings to see which one matches the request. A HandlerMapping can do anything, but usually calls UrlPathHelper#getLookupPathForRequest to transform the request into a path.
The default behavior of the UrlPathHelper depends on the <servlet-mapping> used:
if you used an exact mapping as in the case of /test, a request for /test will give a path of /test,
if you used a prefix mapping /api/*, a request for /api/foo/bar will result in a path of only /foo/bar.
As mentioned in the comment by M. Deinum you need to set the alwaysUseFullPath property to true on the RequestMappingHandlerMapping instance used by the DispatcherServlet.
In your XML application context configuration you define a bean of class RequestMappingHandlerMapping, while you should overwrite the bean used by the DispatcherServlet. Therefore you need to use the same id internally used by Spring:
<mvc:annotation-driven/>
<bean id="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
</bean>
Remark: This solution should work unless you invert the order of the definitions in your XML. In such a case the default definition will overwrite yours. Set the logger org.springframework.beans.factory.support do DEBUG to check which definition overwrites the other. In one case you get:
Overriding user-defined bean definition for bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' with a framework-generated bean definition: ...
in the other:
Overriding bean definition for bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' with a different definition: ...
I am developing a web app using Spring mvc. I have page which displays all projects in DB. If i click on any of the projects listed, it will display some other additional details of that particular project. This is done by using #PathVariable.
#RequestMapping(value={"/project/{name}"})
public String viewProject(HttpServletRequest request,#PathVariable("name")
String projectName, ModelMap model){
.......
.......
}
Above is my request mapping code. My url will be http://localhost:8083/releaseDashboard/project/CSOB.html (csob is my project name and releaseDashboard is my app name). Till this my app works fine. When i click on the home button from this page, my request is mapped to the above controller method and my url becomes localhost:8083/releaseDashboard/project/home.html. But the expected url is localhost:8083/releaseDashboard/home.html
Can anyone please help me? I read that we can use Interceptor or Filters to change the requested url. But i couldnt see any code snippet for that.
UPDATE
Web.xml
<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>*.html</url-pattern>
</servlet-mapping>
spring-servlet.xml
<context:component-scan base-package="com.suntec.reldashboard.controller" />
<context:component-scan base-package="com.suntec.reldashboard.service" />
<context:component-scan base-package="com.suntec.reldashboard.dao" />
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Your config in web.xml in the context of Spring MVC is incorrect.
Edit it as <url-pattern>/</url-pattern>. By this, all the requests to your project will pass through the 'dispatcher servlet'.
(You can also use like this <url-pattern>something-here</url-pattern>. Then your base url should have an extra 'something-here').
Now you can access the resource,
#RequestMapping(value={"/project/{name}"})
public String viewProject(HttpServletRequest request,#PathVariable("name")
String projectName, ModelMap model){
.......
.......
return "hello";
}
by the URL http://localhost:8083/releaseDashboard/project/CSOB. Then projectName will be CSOB.
You must have a 'jsp' file under /WEB-INF/jsp/ having the name hello.jsp. In that jsp file, you can access model values.
You must not use .html/.jsp within the URL, when using Spring MVC. All the resource is bounded to a VIEW, using view resolver. That is how this must be implemented. That's for it is 'MVC' and 'view-resolving'.
NOTE:
As per your current configuration, "you have to change a requested URL". NO, you can't. Then your URL may be http://localhost:8083/releaseDashboard/project/CSOB.html; and projectName is "CSOB.html". Then you have to use java substring function to extract "CSOB" from "CSOB.html". And this is an ugly stuff!
I have configured spring jpa with annotation driven. I would expect the following code to persist the changes to the database upon method exist.
#Transactional
public Foo changeValue(int id){
final Foo foo = fooRepository.findOne(id);
if(foo != null){
foo.setValue("new value");
//fooRepository.save(foo);
}
}
FooRepository is a JPARepository and the foo object is getting fetched so it is managed. Based on what I read about #Transactional I would expect that even without the fooRepository.save(foo) call the changes in foo's value column in the database would be persisted upon method exist. However, this only happens if I uncomment the call to fooRepository.save(foo)
No exceptions are thrown and the configuration for the jpa datasource is as below.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan
base-package="com.example.package.data" />
<jpa:repositories
base-package="com.example.package.data.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager" />
Any ideas?
Update
I do have a ContextLoaderListener and a DispatcherServlet but I do not have component scan for both.
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
</servlet>
....
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Update
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
And there I do
<context:component-scan base-package="com.example.package">
<context:exclude-filter type="regex"
expression="com.example.package.data.*" />
<context:exclude-filter type="regex"
expression="com.example.package.web.controller.*" />
<context:exclude-filter type="regex"
expression="com.example.package.web.service.*" />
</context:component-scan>
And in servlet-context
<context:component-scan base-package="com.example.package.web" />
Your general approach is correct. The changes should get saved and committed on exit of the method. While this approach works great when it works it is a bitch to debug.
Your configuration looks ok to me, so I would double check, that you actually have Spring Beans at your disposal and not some instance that you created simply by calling the constructor.
To verify this, just put a breakpoint at the code point where the annotated method gets called. The bean with the annotated method should NOT by of the class you wrote, but some ProxySomething class.
This article also provides some pointers what might be wrong with your setup.
If you have both a ContextLoaderListener and DispatcherServlet they both load a configuration file.
Judging from your configuration the services are loaded by the DispatcherServlet and your <tx:annotation-driven /> is loaded by the ContextLoaderListener. They both create an ApplicationContext and as AOP is only applied to the beans in the same context your services will not be transactional (Transaction management is done by using AOP).
As a rule of thumb your ContextLoaderListener should contain all shared or global resources (like services, daos, datasources etc.) whereas your DispatcherServlet should only contain web related beans like controllers, view resolvers etc.
I'm learning Spring by integrating Spring 3 into a legacy Servlet application and gradually converting the legacy app over.
A web.xml *-servlet.xml similar to the ones I am using are posted below. Basically things are set up such that retrieving a string like "search", Spring will route it to a Controller and the view resolver will convert "search" into "/jsp/search.jsp"
I ran into problems doing a response.sendRedirect("search") from a legacy Servlet and a legacy ServletFilter To Spring. The URL came out correctly, but I got a blank page despite System.out.println() calls indicating that the JSP was reached. No error messages from Spring and the browser only told me something went wrong with the redirect.
I fixed that problem by forwarding, instead of redirecting from the legacy Servlets and ServletFilters:
request.getRequestDispatcher("search").forward(request,response);
getServletConfig().getServletContext().getRequestDispatcher("search").forward(request,response);
Going in the OTHER direction from a new JSP done in Spring 3 to a legacy Servlet, I have buttons on screens so I just used a javascript call to "location.href=/helloworld". I will need to send some parameters, so I will likely convert those buttons into submitting tiny HTML forms.
What I am wondering is, is there a better approach to getting Spring 3 and the Legacy Servlets communicating in a better way.
Legacy Servlet => Spring 3
and
Spring 3 => Legacy Servlet
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Acme</display-name>
<!--welcome-file-list>
<welcome-file>/login</welcome-file>
</welcome-file-list-->
<servlet>
<servlet-name>acme</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>acme</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Help Find The Spring Config Files -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/nsd-servlet.xml,
/WEB-INF/nsd-security.xml
</param-value>
</context-param>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Integrate A Legacy Screen Done With A Servlet -->
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>
com.legacy.HelloWorldServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/helloworldservlet</url-pattern>
</servlet-mapping>
</web-app>
My acme-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
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">
<context:component-scan base-package="com.acme.controller" />
<mvc:resources mapping = "/**" location = "/,file:/apps1/bea/user_projects/domains/acme/common/,file:/c:/weblogic_common_files/acme/"/>
<mvc:annotation-driven/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/jsp/"/>
<property name = "suffix" value = ".jsp"/>
</bean>
</beans>
When you use response.sendRedirect(String url) you are essentially passing in a URL that you want to redirect to.
sendRedirect observes the following rules:
/*
* Destination, it can be any relative or context specific path.
* if the path starts without '/' it is interpreted as relative to the current request URI.
* if the path starts with '/' it is interpreted as relative to the context.
*/
String destination ="/jsp/destination.jsp";
response.sendRedirect(response.encodeRedirectURL(destination));
When you type in "search", this is relative to the current request URI. Thus, unless your current request was to /acme/something, and assuming /acme/search is your servlet, the request will fail.
However, if you put the path relative to the root, using /acme/search, then the request will work from any context.
With that said, I'm not convinced this is really the best approach, as a redirect involves sending a response back to the client browser telling it to once again fetch content from yet another URL. This seems like a wasted trip to and from the server.
A better method may be to just wire in your servlets into Spring using something like Spring MVC. It's pretty flexible in that you can wrap new controller classes around your existing servlets and then invoke them directly by passing in the HttpServletRequest and HttpServletResponse objects. Once done, you can then slowly eliminate anything redundant with a working, efficient system instead of one chained together with redirects.
package samples;
public class SampleController extends AbstractController {
private int cacheSeconds;
// for wiring in cacheSeconds
public void setCacheSeconds(int cacheSeconds) {
this.cacheSeconds = cacheSeconds;
}
public int getCacheSeconds() {
return cacheSeconds;
}
public ModelAndView handleRequestInternal(
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// you now have a Spring Controller wired in, and you could delegate to
// your legacy servlet in this manner
YourServlet servlet = new YourServlet();
servlet.doGet(request, response);
ModelAndView mav = new ModelAndView("hello");
mav.addObject("message", "Hello World!");
return mav;
}
}
<bean id="sampleController" class="samples.SampleController">
<property name="cacheSeconds" value="120"/>
</bean>
Note that manually instantiating a servlet is not really considered a good practice or good design. Instead, this is more of a stepping stone to get your Spring Controllers wired in and connected so that you can then systematically refactor your code and eliminate the legacy servlets by moving your business logic to service layers.
This approach jives with your plan to systematically migrate to and learn more about Spring, tackle some technical debt, all while still working on business goals related to your site.
I have a problem with my Spring application where my #Service classes are being created twice when the application starts. I know this is a problem with my configuration, as I've experienced it before, but what exactly am I doing wrong?
Is there anything fundamentally wrong with the way I've laid out my config, below? (I have omitted everything I deem to be irrelevant)
web.xml:
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/myapp-config.xml
/WEB-INF/myapp-security.xml
/WEB-INF/myapp-mvc.xml
</param-value>
</context-param>
<listener>
<listener-class>com.myapp.servlet.MyAppContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
myapp-servlet.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<mvc:annotation-driven />
myapp-config.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<context:annotation-config />
In addition to #GaryF's answer, there is a following beautiful solution for this problem (used in projects generated by Spring Roo):
myapp-config.xml
<!-- Load everything except #Controllers -->
<context:component-scan base-package="com.myapp">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
myapp-servlet.xml
<!-- Load #Controllers only -->
<context:component-scan base-package="com.myapp" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
EDIT:
Removing <context:component-scan> from myapp-config.xml means, that all your autodiscovered annotated beans are registered in DispatcherServlet's context (that is, the context loaded from myapp-servlet.xml).
However the recommended approach is to use servlet's context for presentation-specific things (such as controllers), and use the root context (myapp-config.xml) for the core services of your application. The solution above make this approach easy.
Regarding the practical considerations, when your core services are placed in servlet's application context, they can't be accessed from outside the scope of that servlet, for example, from another servlets (you may need to use another servlets to implement another access technologies) or filters (such as Spring Security filters). That's the reason for having core services in the root application context.
As an addition to the answer #axtavt gave, I’d like to give the matching Spring JavaConfig here.
In RootConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
excludeFilters = #Filter({Controller.class, Configuration.class}))
In WebMvcConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
useDefaultFilters = false, includeFilters = #Filter(Controller.class))
You're doing two separate component-scans over the same base-package. Remove one of them.