I'm new to spring mvc and I wish to create a RESTful service.
I create a controller,maps a function to URL and when I try to access it instead of a JSON I receive a 406 error.
I guess there is some trouble with the configuration(Java configuration) but I can't find what it is.
here is my WebConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.yated.web.controller")
public class WebConfig {
}
here is my WebInitializer:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
and here is the controller:
#RestController
public class EventsController {
#RequestMapping(value = "/events")
public List<String> getEvents()
{
List<String> list = new ArrayList<String>();
list.add("Event1");
list.add("Event2");
return list;
}
}
and as I said when I try to access:
http://localhost:8181/web/events
or
http://localhost:8181/web/events.json
I get http error 406
Any ideas?
Related
I have Spring Boot application, everything works fine until I implement spring security in front of my application. This is a RESTful api that has a token based authentication. What's even more weird it works (!) intermittently - by intermittently I mean restarting the application will return the right responses such as 401/403 if unauthenticated and other codes if user is authorized to access them. This is being deployed into WebLogic.
2017-01-05 14:12:51.164 WARN 11252 --- [ (self-tuning)'] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/user] in DispatcherServlet with name 'dispatcherServlet'
WebApplication.java
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class WebApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
Object[] sources = new Object[2];
sources[0] = WebConfiguration.class;
sources[1] = WebSecurityConfiguration.class;
SpringApplication.run(sources, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
}
WebConfiguration.java
#Configuration
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao"})
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class })
public class WebConfiguration extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(WebConfiguration.class);
/**
* Setup a simple strategy: use all the defaults and return XML by default
* when not sure.
*/
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON).mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
#Bean(name = "entityManagerFactory")
public EntityManagerFactory getQmsEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName(Config.PERSISTENCE_UNIT_NAME);
em.setPersistenceXmlLocation("META-INF/persistence.xml");
em.setDataSource(getDataSource());
em.setJpaVendorAdapter(getJpaHibernateVendorAdapter());
em.afterPropertiesSet();
return em.getObject();
}
#Bean
public HibernateJpaVendorAdapter getJpaHibernateVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(true);
// adapter.setDatabase("ORACLE");
adapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");
return adapter;
}
#Bean(name="dataSource", destroyMethod = "")
//http://stackoverflow.com/questions/19158837/weblogic-datasource-disappears-from-jndi-tree
#Qualifier("dataSource")
#Profile("weblogic")
public DataSource dataSource() {
DataSource dataSource = null;
JndiTemplate jndi = new JndiTemplate();
try {
dataSource = (DataSource) jndi.lookup("jdbc/datasource");
} catch (NamingException e) {
logger.error("NamingException for jdbc/datasource", e);
}
return dataSource;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
};
}
}
WebSecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan({
"com.subject",
"com.custom"
})
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private StatelessAuthenticationFilter statelessAuthenticationFilter;
#Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CusAuthenticationProvider cusAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(cusAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.securityContext()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(statelessAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
}
}
StatelessAuthenticationFilter.java
#Component
public class StatelessAuthenticationFilter extends OncePerRequestFilter {
#Inject
private SubjectLookupService subjectLookupService;
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authenticateUser(request));
filterChain.doFilter(request, response);
}
private Authentication authenticateUser(HttpServletRequest request) {
try {
String application = StringUtils.defaultString(request.getParameter("application"));
UserInfo me = subjectLookupService.getUserInfo();
List<GrantedAuthority> roles = me.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())).collect(Collectors.toList());
UserDetails user = new User(me.getUsername(), "", roles);
Authentication authentication = new UserAuthentication(user);
return authentication;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Controller.java
#RestController
public class Controller {
#Autowired
private QService qService;
#PreAuthorize("hasAnyRole('view', 'admin')")
#RequestMapping(value = "/q/{year}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> listQuotas(#PathVariable Integer year) {
return new ResponseEntity<>(qService.listQs(year), HttpStatus.OK);
}
#RequestMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> user(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new ResponseEntity<>( auth.getPrincipal(), HttpStatus.OK);
}
#PreAuthorize("hasRole('shouldntauthorize')")
#RequestMapping(value = "/unauthorized/{year}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> unauthorized(#PathVariable Integer year) {
return new ResponseEntity<>(qService.listQs(year), HttpStatus.OK);
}
}
When it works - I am able to hit any of the above methods using HTTP gets and I am getting correct responses. When it's not working, I am constantly getting:
2017-01-05 14:18:47.506 WARN 11252 --- [ (self-tuning)'] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/user] in DispatcherServlet with name 'dispatcherServlet'
I can verify in the logs that when Spring Boot initializes the application is also sets the correct mapping URL.
Any ideas what could be the problem here?
when you say "intermittently" I tend to think that the problem is with Spring startup configuration.
So, I'd be weary on the fact that you have #ComponentScan twice, and with different packages.
Could you try removing
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao"})
from class WebConfiguration.java and
#ComponentScan({ "com.subject", "com.custom" })
from class WebSecurityConfiguration.java, and replace them with a single
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao", "com.subject", "com.custom"})
in the main SpringBoot class?
I have a nano HTTP based web server, that is supposed to delegate its calls to a jersey 2.22.2. On the webserver class constructor I declare an ApplicationHandler as a instance variable:
ApplicationHandler newHandler;
Then in the constructor I initilize it and register a Sample resource class:
Object[] instances = new Object[1];
instances[0] = new SampleResource();
ResourceConfig app = new ResourceConfig();
app.registerInstances(instances);
newHandler = new ApplicationHandler(app);
On the method that processes Http requests I create a ContainerRequest and execute the apply method on the application handler :
SecurityContext secContext = new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "user";
}
};
}
#Override
public boolean isUserInRole(String s) {
return true;
}
#Override
public boolean isSecure() {
return true;
}
#Override
public String getAuthenticationScheme() {
return null;
}
};
PropertiesDelegate propertiesDelegate = new PropertiesDelegate() {
Map<String, Object> props = new HashMap<>();
#Override
public Object getProperty(String s) {
return props.get(s);
}
#Override
public Collection<String> getPropertyNames() {
return props.keySet();
}
#Override
public void setProperty(String s, Object o) {
props.put(s, o);
}
#Override
public void removeProperty(String s) {
props.remove(s);
}
};
ContainerRequest request = new ContainerRequest(new URI("http://localhost:2000"), new URI("/test"), session.getMethod().toString(), secContext, propertiesDelegate);
Future<ContainerResponse> responseFuture = newHandler.apply(request);
ContainerResponse response = responseFuture.get();
Object entity = response.getEntity();
Below is the code for the SampleResource class :
public class SampleResource {
#GET
#Path("test")
public Response testMethod() {
return Response.status(Response.Status.OK).build();
}
}
The main reason for doing this is that I want to call a custom API that injects objects into the annotated resource classes.
Stepping through the code, all I get is a NotFoundException.
If you want to inject custom Objects to the resources class you can do that in two waus
using #Context -- By adding your custom object to application context
usign #Inject -- By binding the application to resource config
to use #Context , you need to extend the java.security.Principal object and declare your object fields, and you can instantiate and assign values by using security context.
to user #InJect , you need to register org.glassfish.hk2.utilities.binding.AbstractBinder like below
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(ObjectThatneedtoInject.class).to(yourClass.class);
}
});
}
}
I try to save a LocalTime (joda) field to the MongoDB with SpringData using spring-boot-starter-parent (org.springframework.boot 1.2.3.RELEASE) and get a StackOverflowError.
The StackOverflowError is in BeanWrapper in the method
public <S> S getProperty(PersistentProperty<?> property, Class<? extends S> type)
Stacktrace:
http-nio-8080-exec-2#5509 daemon, prio=5, in group 'main', status: 'RUNNING'
at org.springframework.data.mapping.model.BeanWrapper.getProperty(BeanWrapper.java:120)
at org.springframework.data.mapping.model.BeanWrapper.getProperty(BeanWrapper.java:100)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:419)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:412)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:307)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:412)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:511)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:424)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:412)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:307)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:412)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:511)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:424)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:412)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:307)...
Adding these two Converters to the CustomConversions fix the problem.
#Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "databasename";
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient("localhost");
}
#Override
public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new LocalTimeToStringConverter());
converters.add(new StringToLocalTimeConverter());
return new CustomConversions(converters);
}
}
public class LocalTimeToStringConverter implements Converter<LocalTime, String> {
#Override
public String convert(LocalTime localTime) {
return localTime.toString();
}
}
public class StringToLocalTimeConverter implements Converter<String, LocalTime> {
#Override
public LocalTime convert(String s) {
return LocalTime.parse(s);
}
}
I want to know the purpose of below methods inside the class when the class extends AbstractAnnotationConfigDispatcherServletInitializer
protected Class[] getRootConfigClasses() {
return new Class[0];
}
#Override
protected Class[] getServletConfigClasses() {
return new Class[] { testAPI.class };
}
3.#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
(1) wants a list of #Configuration (user) classes for creating a Root ApplicationContext and (2) wants a list of (user) classes for the AnnotationConfigWebApplicationContext.
So image you have several #Configuration classes coded somewhere, you can register them with:
#Override
protected final Class<?>[] getRootConfigClasses() {
return new Class[]{CoreConfiguration.class, JpaConfiguration.class,
ShiroSecurityConfig.class};
}
I have written a junit to test my rest service offline.The junit for my restful controller extends AbstractControllerTestSupport which is used to create the dispatcherservletinstance.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=MockWebContextLoader.class, locations={"/rest-servlet- test.xml"})
public abstract class AbstractControllerTestSupport extends TestCase {
private static DispatcherServlet dispatcherServlet;
....
public static DispatcherServlet getServletInstance() {
if(null == dispatcherServlet) {
dispatcherServlet = new DispatcherServlet() {
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
return MockWebContextLoader.getInstance();
}
};
System.out.println("dispatcher:"+dispatcherServlet.getContextConfigLocation()+":"+dispatcherServlet.getWebApplicationContext());
try {
dispatcherServlet.init(new MockServletConfig());
} catch (ServletException se) {
System.out.println("Exception"+se.getMessage());
}
}
return dispatcherServlet;
}
Following is my loader class.
public class MockWebContextLoader extends AbstractContextLoader {
public static final ServletContext SERVLET_CONTEXT = new MockServletContext(
"/mHealthAPIs", new FileSystemResourceLoader());
private final static GenericWebApplicationContext webContext = new GenericWebApplicationContext();
protected BeanDefinitionReader createBeanDefinitionReader(
final GenericApplicationContext context) {
return new XmlBeanDefinitionReader(context);
}
public final ConfigurableApplicationContext loadContext(
final String... locations) throws Exception {
SERVLET_CONTEXT.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
webContext);
webContext.setServletContext(SERVLET_CONTEXT);
createBeanDefinitionReader(webContext).loadBeanDefinitions(locations);
AnnotationConfigUtils.registerAnnotationConfigProcessors(webContext);
webContext.refresh();
webContext.registerShutdownHook();
return webContext;
}
public static WebApplicationContext getInstance() {
return webContext;
}
protected String getResourceSuffix() {
return "-context.xml";
}
the test runs fine with spring version 3.0 .However if I shift to spring 3.2.x it gives me following error "The type MockWebContextLoader must implement the inherited abstract method SmartContextLoader.loadContext(MergedContextConfiguration)" .This is because in 3.2.2 "AbstractContextLoader" implements "SmartContextLoader" .
Can you provide me with the work around?
Got the solution:I changed the MockWebContextLoader class as follows.
public class MockWebContextLoader extends AbstractContextLoader {
public static final ServletContext SERVLET_CONTEXT = new MockServletContext(
"/mHealthAPIs", new FileSystemResourceLoader());
private final static GenericWebApplicationContext webContext = new GenericWebApplicationContext();
protected BeanDefinitionReader createBeanDefinitionReader(
final GenericApplicationContext context) {
return new XmlBeanDefinitionReader(context);
}
#Override
public ApplicationContext loadContext(MergedContextConfiguration arg0)
throws Exception {
SERVLET_CONTEXT.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
webContext);
webContext.setServletContext(SERVLET_CONTEXT);
createBeanDefinitionReader(webContext).loadBeanDefinitions(
arg0.getLocations());
AnnotationConfigUtils.registerAnnotationConfigProcessors(webContext);
webContext.refresh();
webContext.registerShutdownHook();
return webContext;
}
public static WebApplicationContext getInstance() {
return webContext;
}
protected String getResourceSuffix() {
return "-context.xml";
}
public final ConfigurableApplicationContext loadContext(
final String... locations) throws Exception {
return null;
}
}