I'm using Spring Rest controller for my Restful calls. I'm having Spring 4.3.x version of JAR's. When I run the project itself, the index.jsp is not getting called. I've not configured anything in xml because I'm using annotation method. Here are my files.
P.S : I'm not using Maven, its a dynamic web project and all Spring JAR's (Webmvc, web, core, context, beans) are added to build path.
I've followed http://viralpatel.net/blogs/spring-4-mvc-rest-example-json/
AppConfig
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "net.ifg.spring")
public class AppConfig {
}
AppInitializer
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
#Override
protected Class[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
CustomerDAO
public class CustomerDAO {
// Dummy database. Initialize with some dummy values.
private static List<Customer> customers;
{
customers = new ArrayList();
// Add customers here
}
public List list() {
return customers;
}
}
CustomerRestController
#RestController
public class CustomerRestController {
#Autowired
private CustomerDAO customerDAO;
#GetMapping("/customers")
public List getCustomers() {
return customerDAO.list();
}
}
Web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>IFG</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Why its not able to hit the URL http://localhost:8080/IFG/customers? There should be the issue with AppInitializer file.
Any ideas would be greatly appreciated.
You have to specify IFG in a request mapping. The current link according to your mapping should be http://localhost:8080/customers. Add the #RequestMapping annotation specifying the path.
#RestController
#RequestMapping("/IFG")
public class CustomerRestController
#RestController
#RequestMapping("/IFG")
public class CustomerRestController {
#Autowired
private CustomerDAO customerDAO;
#GetMapping("/customers")
public List getCustomers() {
return customerDAO.list();
}
}
Now hit the url like below :
localhost:port/IFG/customers
To make it work, you should add:
#RestController
#RequestMapping("/IFG")
and check the number of port: 8080 (it could be 9080 or whatever)
Just try access http://localhost:8080/customers (without /IFG), just copied that project locally, on my tomcat, and it works, without running mvn tomcat7:run.
Your setup in web.xml <display-name>IFG</display-name> is not the application context path by which to access.
From the docs:
display-name
The optional display-name element specifies the Web
application display name, a short name that can be displayed by GUI
tools.
After http://localhost:8080 you have to give your app name prior calling the controller. Lets say your app name is myApp, so the url should be:
http://localhost:8080/myApp/customers
provided you use the controller in your question, if you have added to your controller #RequestMapping("/IFG") as per other answers suggested then you have to change the url to:
http://localhost:8080/myApp/IFG/customers
EDIT
I see in your AppInitializer class you are returning null from getServletConfigClasses(). I believe that the AppConfig.class should be returned there.
#Override
protected Class[] getServletConfigClasses() {
return new Class[] { AppConfig.class };
}
Just checked that it was due to javax.servlet.ServletException: Failed to instantiate WebApplicationInitializer class. This I've fixed by adding commons-logging-1.2.jar, spring-aop, spring-expression jars. Atleast I'm able to point to the right method.
Related
I am using a Spring MVC (5.3.7) app which has a Rest Controller. When I deploy the war using Intellij or manually on Tomcat 10.0 server, the get url gives me 404. After trying different Spring MVC configs which offcourse didn't work. Finally I resorted to the following config but still no luck
My DispatcherServletInitializer,
package com.luv2code.springdemo.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MySpringMvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { DemoAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
My DemoConfig class as below used above,
#Configuration
#EnableWebMvc
#ComponentScan("com.luv2code.springdemo")
public class DemoAppConfig implements WebMvcConfigurer {
}
RestController class
#RestController
#RequestMapping("/api")
public class CustomerRestController {
#GetMapping("/customers")
public List<Customer> getCustomers() {
return customerService.getCustomers();
}
}
I have also added index.jsp in webapp folder in classpath which shows the right html (no 404 in route) when war is deployed. Meaning my MVC setup is fine but I don't know why I cannot reach the controller. I am using Java 11, here are the MAVEN dependencies list,
javax.servlet-api - 4.0.1
javax.servlet.jsp-api - 2.3.3
spring-webmvc - 5.3.7 Final
Packaging - war
Thanks for the suggestion. Downgrading to Tomcat#9 has done the work for me. Everything seems to be working now.
Here is the issue link which describes the incompatibility with Spring MVC 5.3.7 and Tomcat#10
Following is a folder hierarchy in the Spring boot Hibernate project ,
This is a method in UserResource Controller
#GetMapping("/")
public ModelAndView home(HttpServletRequest request){
//request.setAttribute("mode", "MODE_HOME");
ModelAndView model = new ModelAndView("index");
model.addObject("msg", "hello world");
return model;
}
Following code also added to the application.properties file to find the correct jsp page.
spring.mvc.view.prefix:WebApp/Web/
spring.mvc.view.suffix:.jsp
But this is not worked and following error is occurred.
Anyway, I tried many ways by modifying the path in the 'application.properties' file and couldn't find the right solution. Are there any steps to fire the jsp view?
If your main class don't extends SpringBootServletInitializer ,try this.
public class SpringHibernateAssignmentApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringHibernateAssignmentApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(SpringHibernateAssignmentApplication.class, args);
}
}
I'm trying to implement simple web service using Spring 4. But my userService doesn't seem to work.
I have configuration like this:
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{ PersistenceConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{
};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/api/*"};
}
}
With Persistence config for my in-memory HSQLDB like this:
#Configuration
#ComponentScan("example.userService")
#PropertySource("classpath:hibernate.properties")
#EnableJpaRepositories("example.userService.repo")
public class PersistenceConfig {
#Autowired
Environment env;
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory() {
/* ... */
return entityManagerFactoryBean;
}
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:db/data.sql")
.build();
}
}
I have an entity User. And here is my repository:
#Repository
#RepositoryRestResource
public interface UserRepo extends CrudRepository<User, Long> {
List<User> findAll();
List<User> findByFirstName(#Param("firstName") String firstName);
}
And I also have na embedded jetty on port 9999 to run this service.
And when jetty starts and i go to localhost:9999/api/users/ or /user/1 or smth like that, an error 404 occurs. findByFirstName method also does not work.
I think this happens because Servlet Dispatcher doesn't know about my exposed repository. But I also don't know how to 'register' it since I don't want to use controller for it.
I have read spring docs here, here and another tutorials but most of them do not provide any configs and describe repositories that simply just work.
Any suggestions on what can I do with it and what can cause this problem since nothing is shown in console and in compile-time everything work fine?
Thanks in advance!
UPD 1:
I've added #RepositoryRestResource to UesrRepo according to Accessing JPA Data with REST but /api/users is still 404.
UPD 2:
I've simplified question by removing service-part of it since it requires controller according to #Antoniossss's comment. Not it's just that simple: Which configuration (if any) should I use to expose repository?
It looks like you missed Spring data rest configuration and have only MVC configuration. You need to add bean RepositoryRestConfigurer to your configuration or extend RepositoryRestConfigurerAdapter.
Could you possibly explain how I can get the ServletContext instance in my Application's sub-class? Is it possible? I have tried to do it like in the following snippet but it does not seem to work - the ctx is not set:
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
//...
#ApplicationPath("/")
public class MainApplication extends Application {
#Context ServletContext ctx;
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
//...
return classes;
}
}
web.xml:
<web-app ...>
<context-param>
<param-name>environment</param-name>
<param-value>development</param-value>
</context-param>
<filter>
<filter-name>jersey-filter</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.MainApplication</param-value>
</init-param>
</filter>
...
</web-app>
The problem is that I need to get context parameters from it. If there is another way, I would be grateful if somebody gave a hint.
I understand that Context annotation might not be purposed for this. Actually, I do not need ServletContext itself. If only I could get context params from web.xml, I would be absolutely happy.
Here is an example of what I really need:
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class MainApplication extends Application {
#Context ServletContext ctx;
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<Object>();
final String environment = ctx.getInitParameter("environment");
//final String environment = ... get context parameter from web xml
set.add(new AbstractBinder() {
#Override
protected void configure() {
bind(new BaseDataAccess(environment)).to(DataAccess.class);
}
});
//...
return set;
}
}
Thanks.
Since Jersey 2.5, ServletContext can be injected directly in constructor:
https://java.net/jira/browse/JERSEY-2184
public class MyApplication extends ResourceConfig {
public MyApplication(#Context ServletContext servletContext) {
// TODO
}
}
#Context can be made available on ResoureConfig by injecting it as a constructor parameter using #Context. Another way to access it is through an event handler.
Try the below code.
#ApplicationPath("...")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(StartupHandler.class);
}
private static class StartupHandler extends AbstractContainerLifecycleListener {
#Context
ServletContext ctx;
#Override
public void onStartup(Container container) {
// You can put code here for initialization.
}
}
// ...
Injection happens when you enter service method. Check if this is a problem.
There is interesting statement in documentation for Jersey version 1.18 for class
com.sun.jersey.spi.container.servlet.ServletContainer
The servlet or filter may be configured to have an initialization
parameter "com.sun.jersey.config.property.resourceConfigClass" or
"javax.ws.rs.Application" and whose value is a fully qualified name of
a class that implements ResourceConfig or Application. If the concrete
class has a constructor that takes a single parameter of the type Map
then the class is instantiated with that constructor and an instance
of Map that contains all the initialization parameters is passed as
the parameter.
If my understanding is correct the following constructor must be invoced with "an instance of Map that contains all the initialization parameters"
public class ExampleApplication extends Application {
public ExampleApplication(Map initParams) {
}
...
}
Here is appropriate part of web.xml:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>experiment.service.ExampleApplication</param-value>
</init-param>
</servlet>
But somehow it failed for me with the following message:
SEVERE: Missing dependency for constructor public
experiment.service.ExampleApplication(java.util.Map) at parameter
index 0
And for current version of Jersey (2.5.1) there are no such statement in documentstion:
https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/servlet/ServletContainer.html
You can use the ApplicationEventListener interface to get the ServletContext. After initialization has finished, you can 'catch' an ApplicationEvent and use the injected ServletContext to work with.
Works fine with: org.glassfish.jersey : 2.12
For additional versions, pls use comments - i dont know, sry.
Jersey Docs - 20.1.2. Event Listeners
Your MainApplication:
#ApplicationPath("/")
public class MainApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(MainApplicationListener.class);
return classes;
}
}
... or alternative MainResourceConfig (I prefer to use this one):
public class MainResourceConfig extends ResourceConfig {
public MainResourceConfig() {
register(MainApplicationListener.class);
}
}
And the ApplicationEventListener:
public class MainApplicationListener implements ApplicationEventListener {
#Context
private ServletContext ctx; //not null anymore :)
#Override
public void onEvent(ApplicationEvent event) {
switch (event.getType()) {
case INITIALIZATION_FINISHED:
// do whatever you want with your ServletContext ctx
break;
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
}
Don't use #Context in your Application but in a Resource class.
#Path("/foos")
public class FooResource {
#Context
ServletContext ctx;
#GET
public Response getFoos() {
return Response.ok().build();
}
}
I have existing web-app which I want to convert into web.xml-less of servlet's 3.0. I've managed to make it working, however there are 2 tags in web.xml which I still don't know the equivalent code in web.xml-less environment.
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/pageNotFound</location>
</error-page>
Any help is appreciated
In Servlets 3.0 you don't need a web.xml for many cases, however, sometimes it's required or just useful. Your case is just one of them - there is no special annotations to define welcome-file list or error-pages.
Another thing is - would you really like to have them hardcoded? There are some valid use-cases for annotation / programmatic based configuration and for declarative configuration in XML. Moving to Servlets 3.0 doesn't necessarily means getting rid of web.xml at all cost.
I would find the entries you posted a better example of configuration in XML. Firstly - they can be changed from deployment to deployment and secondly - they affect whole application and not any particular Servlet.
For analog welcome-page-list put this in
#EnableWebMvc
#Configuration
#ComponentScan("com.springapp.mvc")
public class MvcConfig extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/*.html").addResourceLocations("/WEB-INF/pages/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index.html");
}
...
}
In Spring Boot or general Spring MVC app for following scenario:
Static files can be served from locations registered with a custom ResourceHandlerRegistry. We have a static resource index.html and it can accessed at localhost:8080/index.html. We want to just redirect localhost:8080/ request to localhost:8080/index.html, following code will can be used.
package in.geekmj.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/", "/index.html");
}
}
Now accessing localhost:8080/ will redirect to localhost:8080/index.html
In Spring Boot 2.0 you can use this code
#Configuration
public class TomcatInitializer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> , TomcatContextCustomizer {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addContextCustomizers(this);
}
private ErrorPage createStatusErrorPage(int errorCode, String location) {
ErrorPage errorPage = new ErrorPage();
errorPage.setErrorCode(errorCode);
errorPage.setLocation(location);
return errorPage;
}
private ErrorPage createExceptionErrorPage(Class<?> klass, String location) {
ErrorPage errorPage = new ErrorPage();
errorPage.setExceptionType(klass);
errorPage.setLocation(location);
return errorPage;
}
#Override
public void customize(Context context) {
context.addWelcomeFile("/index.jsp");
context.addErrorPage(createStatusErrorPage(404, "/404.jsp"));
context.addErrorPage(createExeptionErrorPage(Exception.class, "exception.jsp"));
context.setSessionTimeout(120);
}
}