Testing Spring managed servlet - java

I need to test a servlet, which is working fine now.
The servlet needs to use a Spring service, so it is modified for that this way:
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(
this, config.getServletContext()); // ImageServlet.java line 49
After migration to Spring 4, the test broke and currently it throws this exception:
java.lang.IllegalStateException:
No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.
getRequiredWebApplicationContext(WebApplicationContextUtils.java:84)
at org.springframework.web.context.support.SpringBeanAutowiringSupport.
processInjectionBasedOnServletContext(SpringBeanAutowiringSupport.java:107)
at package.ImageServlet.init(ImageServlet.java:49)
at in.nasv.utils.ImageServletTest.accessingImageViaHttp(ImageServletTest.java:45)
Here is the portion of code of ImageServletTest:
// prepare servlet instance
MockServletConfig config = new MockServletConfig(
new MockServletContextPatched());
ImageServlet servlet = new ImageServlet();
servlet.init( config ); // ImageServletTest, line 45
And this patched class (is not actually patched now):
public class MockServletContextPatched extends MockServletContext{ }
What am I supposed to do to avoid this "IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?" ?

I found an solution. But clear enough, but an solution.
Now servlet initialization is:
MockServletContext servletContext = new MockServletContextPatched();
MockServletConfig config = new MockServletConfig( servletContext );
ImageServlet servlet = new ImageServlet();
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext( "spring-data-app-context.xml" );
DefaultListableBeanFactory dlbf = new DefaultListableBeanFactory(appContext.getBeanFactory());
GenericWebApplicationContext gwac = new GenericWebApplicationContext(dlbf);
servletContext.setAttribute(GenericWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, gwac);
gwac.setServletContext(servletContext);
gwac.refresh();
servlet.init( config );
Preparing request and response in standard way:
MockHttpServletResponse response = new MockHttpServletResponse();
URL serverUrl = new URL( propertyExtendedService.getServerAddress(true) );
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI( "/what-you-want" );
request.setPathInfo( "/" + TEST_IMAGE );
request.setContentType("image/jpeg");
request.addHeader("Accept", "image/jpeg;image/jpg;" );
Final step is to call the filter and assert returned values:
servlet.doGet( request, response );
assertEquals( response.getStatus(), 200 );
// assert everything you want

Update: the updated documentation for getServletContext() is now online.
It is not necessary to implement a custom MockServletContextPatched class just to configure a custom MIME type in Spring's MockServletContext.
Since Spring's MockServletContext uses the Java Activation Framework (JAF) to implement the ServletContext.getMimeType(String) method, it is quite easy to configure a custom MIME type via JAF's MimetypesFileTypeMap.addMimeTypes(String) method as follows.
MockServletContext mockServletContext = new MockServletContext();
MimetypesFileTypeMap mimetypesFileTypeMap =
(MimetypesFileTypeMap) MimetypesFileTypeMap.getDefaultFileTypeMap();
mimetypesFileTypeMap.addMimeTypes("text/enigma enigma");
assertEquals("text/enigma", mockServletContext.getMimeType("filename.enigma"));
In the above JUnit based test code, I configured a custom MIME type "text/enigma" for files that have the extension .enigma.
Hope this helps!
Regards,
Sam (author of the Spring TestContext Framework)
p.s. I created JIRA issue SPR-12126 in order to improve the documentation of MockServletContext.

Related

Jersey +Grizzly - #ApplicationPath ignored

I'm running Jersey 2.26-b09 on top of Grizzly, and I'm using the following code to start the Grizzly HTTP server:
public void start() {
URI uri = UriBuilder.fromPath("").scheme("http").host("localhost").port(8084).path("/rest").build();
Map<String, String> params = new HashMap<>(16);
String applicationClassName = RestApplication.class.getName();
String applicationPackageName = RestApplication.class.getPackage().getName();
String productionPackageName = ProductionService.class.getPackage().getName();
params.put(ServletProperties.JAXRS_APPLICATION_CLASS, applicationClassName);
params.put(ServerProperties.PROVIDER_PACKAGES, productionPackageName + "," + applicationPackageName);
HttpServer server = GrizzlyWebContainerFactory.create(uri, params);
server.start();
}
The RestApplication class extends Application, and has a #ApplicationPath("/system") annotation.
The ProductionService class is a REST resource with a #Path("/production") annotation.
I can see that the path specified in the #ApplicationPath is ignored: my resources can be accessed at /rest/production and not at /rest/system/production.
I've tried to change the URI to /rest/system instead of /rest, but to no avail:
URI uri = UriBuilder.fromPath("").scheme("http").host("localhost").port(8084).path("/rest/system").build();
The application is deployed in the root context /rest, not /rest/system.
What am I missing?
Of course as a workaround I could change the resource path from "/production" to "/system/production", but I would like to know why the application path is ignored.
I've changed the code that creates and initializes the server to:
public void start() {
URI uri = UriBuilder.fromPath("").scheme("http").host("localhost").port(8084).build();
Map<String, String> params = new HashMap<>(16);
String applicationPackageName = RestApplication.class.getPackage().getName();
String productionPackageName = ProductionService.class.getPackage().getName();
params.put(ServerProperties.PROVIDER_PACKAGES, productionPackageName + "," + applicationPackageName);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
WebappContext context = new WebappContext("system", "/rest/system");
ServletRegistration registration = context.addServlet("jersey", ServletContainer.class);
registration.setInitParameters(params);
registration.addMapping("/*");
context.deploy(server);
server.start();
}
A Web Application context is created and serves the resources at the desired path. Since the servlet container initializer is not invoked in this programmatic approach, the ServletProperties.JAXRS_APPLICATION_CLASS property is not set.
I thought that setting this property were doing the job, but it does not. Thanks to #peeskillet for the hint.

How to set ServletMapping path pattern in context (Tomcat Context)

I create a context and assign as tomcat context with urlpattern /api.
Now, I add DefaultServlet to Tomcat and create a filter and map for jersey resourceConfig.
Context context = tomcat.addContext("/api", base.getAbsolutePath());
Tomcat.addServlet(context, "default", new DefaultServlet());
context.addServletMapping("/*", "default");
final FilterDef def = new FilterDef();
final FilterMap map = new FilterMap();
def.setFilterName("jerseyFilter");
def.setFilter(getJerseyFilter());
context.addFilterDef(def);
map.setFilterName("jerseyFilter");
map.addURLPattern("/*");
context.addFilterMap(map);
tomcat.start();
private static Filter getJerseyFilter(){
final ResourceConfig config = new ResourceConfig()
.packages(Main.class.getPackage().getName())
// create instance of Resource and dynamically configure to ResourceConfig
.register(new Resource(new Core(), configuration))
.register(JspMvcFeature.class) // register jspMVC
.property(ServletProperties.FILTER_FORWARD_ON_404, true);
return new ServletContainer(config);
}
Now, I am successfully able to access localhost:PORT/api/<end_point>
Now my question is, I want to assign a path /serve/* in ServletMapping like
Tomcat.addServlet(context, "default", new DefaultServlet());
context.addServletMapping("/serve/*", "default");
Then I want to access the resource on localhost:PORT/api/serve/<end_point>
but it prompt error
HTTP Status 404 -
type Status report
message
description The requested resource is not available.
Apache Tomcat/8.0.26

List all rest service urls in spring boot cxf

is it possible to list all REST services when using cxf with spring-boot? I've created ApplicationListener<ContextRefreshedEvent> and in there I would like to list all REST service urls which were registered for my cxf servlet. I've tried to poke around CXFServlet, ServletContext, cxf Endpoint and cxf Server classes but I can't figure it out. I've also tried to review wadl generator (feature) and swagger2 feature but they create url and content when request comes. Is it possible?
Thanks.
I would scan the #WebService annotations on the classpath, maybe it will help you:
#Autowired
private ClassPathScanningCandidateComponentProvider annotationScanner;
public List<ClassDocument> generate(String basePackage) throws ClassNotFoundException {
Set<BeanDefinition> candidateComponents = annotationScanner.findCandidateComponents(basePackage);
List<ClassDocument> classDocuments = new ArrayList<>();
for (BeanDefinition component : candidateComponents) {
ClassDocument classDocument = new ClassDocument();
Class<?> beanClass = Class.forName(component.getBeanClassName());
classDocument.setClassName(beanClass.getName());
String[] baseUrl = beanClass.getAnnotation(javax.jws.WebService.class).value();
addMethods(classDocument, beanClass, baseUrl);
classDocuments.add(classDocument);
}
return classDocuments;
}

Trigger manually a FilterChainProxy

I've created manually a new spring FilterChainProxy:
private FilterChainProxy getCustomFilterChainProxy()
{
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>();
securityFilterChains.add(new DefaultSecurityFilterChain( new AntPathRequestMatcher("/**"), new MyFilter1()));
securityFilterChains.add(new DefaultSecurityFilterChain( new AntPathRequestMatcher("/admin/**"), new MyFilter2()));
return new FilterChainProxy(securityFilterChains);
}
And considering that I have a new HttpServletRequest, I would like to check the request against the custom FilterChainProxy. Something like this:
FilterChainProxy customFilterChainProxy = getCustomFilterChainProxy();
customFilterChainProxy.doFilter(request, null, (FilterChain) customFilterChainProxy.getFilterChains() );
But I'm struggling in correctly define the FilterChain used in the 3rd parameter of the doFilter() method.
How can I do this ?
Thanks.
Why don't you write an integration test, using MockMvc with .apply(SecurityMockMvcConfigurers.springSecurity()) Here is a link to an post about integration MockMvc and SpringSecurity.
It is extremely useful, and allows you to test your login, and protected services. I often find myself writing/modifying the test before I try to see if it works in a browser.

How to call .addfilter() while migrating from jetty 7 to jetty 8?

I am in process of migrating jetty 7 to jetty 8,
I am getting this error:
no suitable method found for
addFilter(Class,String,int)
servletContext.addFilter(JsonTokenFilter.class, "/*", FilterMapping.REQUEST);
What I am trying to do is:
// servlet context
ServletContextHandler servletContext = new ServletContextHandler( ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
// set session manager
SessionHandler sessionHandler = new GatewaySessionHandler(sessionManager);
servletContext.setSessionHandler(sessionHandler);
sessionHandler.addEventListener(new SessionListener());
sessionHandler.addEventListener(new CsrfGuardHttpSessionListener());
sessionManager.setSecureRequestOnly(Utilities.getConfigBoolean("fievel.jetty.secureCookies", true));
sessionManager.setHttpOnly(Utilities.getConfigBoolean( "fievel.jetty.httpCookies", true));
setSessionSettings();
// add json rpc signal if session token invalid
servletContext.addFilter(JsonTokenFilter.class, "/*", FilterMapping.REQUEST);
The last line is the one that is giving error.
If I refer ServletContextHandler for jetty 8,
addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches)
is ideally the type of code that I should write.
Can anybody give me a clue where am I going wrong?
i think you should be writing something like this:
servletContext.addFilter(JsonTokenFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
since it expects an EnumSet instead of int
I've tried the tutorial from eclipse jetty tutorial
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContext.setContextPath("/");
server.setHandler(servletContext);
servletContext.addServlet(new ServletHolder(new HelloServlet()),"/*");
servletContext.addFilter(TestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
server.start();
server.join();
}
the output is:
2016-08-10 15:13:58.054:INFO:oejs.Server:jetty-8.1.19.v20160209
TestFilter Init
2016-08-10 15:13:58.186:INFO:oejs.AbstractConnector:Started SelectChannelConnector#0.0.0.0:8080
TestFilter doFilter running
so the filter is registered for sure in this way

Categories

Resources