Dependency injection in servlet on embedded Jetty - java

I have embedded Jetty server and I added servlet mapping.
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(RegisterServlet.class, "/user/register");
I want to make the Dependency Injection in servlet with spring framework configuring ApplicationContext.xml. It should work the same as here:
public class RegisterServlet extends HttpServlet {
private Service service;
#Override
public void init() throws ServletException {
super.init();
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
service = context.getBean("service", Service.class);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
...
}
but without using context.getBean("service").

This way you can have the control of servlet instantiation
Server server = new Server(port);
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(new ServletHolder(new RegisterServlet()), "/user/register");
server.setHandler(handler);
server.start();
So now you can get the servlet instance from a DI container of something

Related

REST-requests are not processed after logging in embedded Jetty 9.4.12

I'm trying to log the request body from embedded jetty-server.
I successfully get the body of the POST-request (mainly from https://stackoverflow.com/a/19547549/8916463 and https://stackoverflow.com/a/8972088/8916463)
public class JettyFilter {
public static void main(final String[] args) throws Exception {
Server server = new Server(8080);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
handler.addServletWithMapping(HelloServlet.class, "/*");
handler.addFilterWithMapping(WebServerLoggingFilter .class, "/*",
EnumSet.of(DispatcherType.REQUEST));
server.start();
server.join();
}
public static class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getReader().lines()
.collect(Collectors.joining(System.lineSeparator())));
}
}
public static class WebServerLoggingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("hello from filter");
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
#Override
public void destroy() {}
}
}
The problem is that the POST-requests from the controllers are not called at all when I configure server this way.
But they are properly called when I try logging using NCSA logging, (in such case request body is not logged):
NCSARequestLog requestLog = new NCSARequestLog(os.getDatedFilename());
requestLog.setExtended(true);
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(requestLog);
requestLog.setAppend(true);
HandlerList mainHandlers = new HandlerList();
mainHandlers.addHandler(accessHandler);
mainHandlers.addHandler(new DefaultHandler());
requestLogHandler.setHandler(mainHandlers);
HandlerList topLevelHandlers = new HandlerList();
topLevelHandlers.addHandler(requestLogHandler);
server.setHandler(topLevelHandlers);
My goal is to log the body of the request to some file.
Apparently I miss something very simple and logging of the requests body can be implemented much easier.
PS. Spring is not used.
Any help will be deeply appreciated!

Servlet gets request twice

I couldn't really get specific with a problem, but SOMETIMES, literally from time to time when i start my jetty server, my login servlet gets GET request twice.
When it happens, login page also doesn't get the css because of this
"Resource interpreted as Stylesheet but transferred with MIME type
text/html".
What i know is that when i disable my filter servlet, everything works fine.
Here's how i add stuff into server:
UserService userService = new UserService();
CharactersService charactersService = new CharactersService();
SessionsService sessionsService = new SessionsService();
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.addServlet(new ServletHolder(new LoginServlet(userService, charactersService, sessionsService)), "/login");
context.addServlet(new ServletHolder(new MainMenuServlet(sessionsService)), "/main");
context.addServlet(new ServletHolder(new DuelServlet(userService)), "/duel");
context.addServlet(new ServletHolder(new FightServlet(charactersService, sessionsService, userService)),"/duel/fight");
context.setContextPath("/");
context.setBaseResource(Resource.newResource("src/main/resources"));
server.setHandler(context);
ServletHolder defaultHolder = new ServletHolder("default", new DefaultServlet());
defaultHolder.setInitParameter("dirAllowed", "true");
context.addServlet(defaultHolder, "/");
context.addFilter(new FilterHolder(new AuthenticationFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
server.start();
server.join();
If i start my server and see that login page get's called twice and doesn't load css, i comment line with addFilter() method, start server, shut it down, uncomment, start it again and login pages loads just fine, which is quite weird.
Is there something wrong with how i add all the stuff to server, or what may even cause such behavior?
Filter servlet code:
public class AuthenticationFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String uri = req.getRequestURI();
HttpSession session = req.getSession(false);
if (session == null && !uri.equals("/login")) {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.sendRedirect("/login");
} else if (session != null && uri.equals("/login")) {
resp.setStatus(HttpServletResponse.SC_OK);
resp.sendRedirect("/main");
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {}
}
I think you are not writing logic of Server creation in a static context (static method/block).
Hence, for each request a new server is created. Which results in 2 requests.
Here is an example of creating a server in static method.

jhipster injection on servlet

I created a servlet, but I'm not able to do the injection of a repository object. Has anyone had this problem?
my config in WebConfigurer:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles()));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebMvcAutoConfiguration.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
my servlet:
#WebServlet(urlPatterns = "/servlet/preview")
public class ServletPreViewHtml extends HttpServlet {
private static final long serialVersionUID = 1L;
#Autowired
private PagecontentRepository pagecontentRepository;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
doGet(request,response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Pagecontent p = pagecontentRepository.getOne(2l); //line 34
out.println(p.getFullHtml());
always returns me the following error:
java.lang.NullPointerException: null at
br.com.dmsolutions.netfarmacontentmanager.web.rest.ServletPreViewHtml.doGet(ServletPreViewHtml.java:34)
line of NPE: pagecontentRepository.getOne(2L);
I agree with Gaƫl Marziou, but you can also use #ServletComponentScan, part of Spring Boot 1.3.
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.3-Release-Notes#support-for-webservlet-webfilter-and-weblistener
Rather than using #WebServlet, I would rather use #Controller so that Spring can inject your repository.

Java Spring how to force https ssl in webapplicationinitializer?

To force https in web.xml i was using this code snippet:
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
Is there an equivalent for this in Spring Java Config? I already figured out that i need a ServletSecurityElement. But how do i connect it to the rest?
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
container.addListener(new ContextLoaderListener(context));
context.register(PersistenceJPAConfig.class);
FilterRegistration filter = container.addFilter("wicket.myproject", WicketFilter.class);
filter.setInitParameter("applicationClassName", WicketApplication.class.getName());
filter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*");
filter.addMappingForUrlPatterns(null, false, "/*");
HttpConstraintElement forceHttpsConstraint = new HttpConstraintElement(ServletSecurity.TransportGuarantee.CONFIDENTIAL, "");
ServletSecurityElement securityElement = new ServletSecurityElement(forceHttpsConstraint);
}
}
As John Thompson pointed out you were right there. You just needed to add the security element you defined to the servlet. On another note I noticed you had "" as the roleNames parameter to the HttpConstraintElement. This would actually cause everyone who didn't have the role "" to be denied. If you want this to work like normal (force https) don't give any roles. In the end this worked for me:
public class ApplicationInitializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final String DISPATCHER_SERVLET_MAPPING = "/";
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(ApplicationConfiguration.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);
// Force HTTPS, and don't specify any roles for this constraint
HttpConstraintElement forceHttpsConstraint = new HttpConstraintElement(ServletSecurity.TransportGuarantee.CONFIDENTIAL);
ServletSecurityElement securityElement = new ServletSecurityElement(forceHttpsConstraint);
// Add the security element to the servlet
dispatcher.setServletSecurity(securityElement);
}
}
I think you need to get a handle on the servlet registration, then register the security element. Try something like this:
ServletRegistration.Dynamic registration
= container.addServlet("dispatcher", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.setServletSecurity(securityElement); //your prev defined securityElement
In the case if you use Spring Security 3.2 you could do this as follows.
<security:intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https"/>
with http to https port mappings as well.
<security:port-mappings>
<security:port-mapping http="${port.mapping.http.port}"
https="${port.mapping.https.port}" />
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final String DISPATCHER_SERVLET_MAPPING = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ApplicationContext.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME,
new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);
HttpConstraintElement forceHttpsConstraint = new HttpConstraintElement(TransportGuarantee.CONFIDENTIAL);
ServletSecurityElement securityElement = new ServletSecurityElement(forceHttpsConstraint);
dispatcher.setServletSecurity(securityElement);
}
What do you mean, connect it to the rest? Looks like you should be set. Spring will auto-detect the configuration of the Java configured WebApplicationInitializer.
Remember that WebApplicationInitializer implementations are detected
automatically -- so you are free to package them within your
application as you see fit.
See:
http://docs.spring.io/spring-framework/docs/3.1.x/javadoc-api/org/springframework/web/WebApplicationInitializer.html#onStartup(javax.servlet.ServletContext)
One way of do this is creating an HTTP filter inside your application:
#Component
#ConfigurationProperties("security.http")
public class ForceHTTPSFilter implements Filter {
public static final String X_FORWARDED_PROTO_HEADER = "x-forwarded-proto";
private boolean forceHttps = false;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(forceHttps && !request.getProtocol().toUpperCase().contains("HTTPS") && request instanceof HttpServletRequest) {
Optional<String> protocol = Optional.ofNullable(((HttpServletRequest)request).getHeader(X_FORWARDED_PROTO_HEADER));
if(!protocol.orElse("http").equals("https")){
((HttpServletResponse)response).sendError(HttpStatus.FORBIDDEN.value(), "Please use HTTPS when submitting data to this server.");
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
public boolean isForceHttps() {
return forceHttps;
}
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
}
You can switch on/off the filter with a property by using #ConfigurationProperties.
Moreover, you should inspect the header x-forwarded-proto because some proxies (like Heroku) remove the protocol from the URL and store it into this header.
And, of course here's a unit test of this filter:
public class ForceHTTPSFilterTest {
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#InjectMocks
private ForceHTTPSFilter filter;
#Test
public void testAcceptHTTPRequestWhenFlagIsDisabled() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTP/1.1");
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, times(1)).doFilter(any(), any());
verify(response, never()).sendError(eq(403), anyString());
}
#Test
public void testAcceptHTTPRequestWhenFlagIsEnableAndItHasForwardedProtoHeader() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTP/1.1");
when(request.getHeader(ForceHTTPSFilter.X_FORWARDED_PROTO_HEADER)).thenReturn("https");
HttpServletResponse response = mock(HttpServletResponse.class);
filter.setForceHttps(true);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, times(1)).doFilter(any(), any());
verify(response, never()).sendError(eq(403), anyString());
}
#Test
public void testAcceptHTTPSRequest() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTPS/1.1");
HttpServletResponse response = mock(HttpServletResponse.class);
filter.setForceHttps(true);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, times(1)).doFilter(any(), any());
verify(response, never()).sendError(eq(403), anyString());
}
#Test
public void testRejectHTTPRequestWhenFlagIsEnableAndItDoesntHaveForwardedProtoHeader() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getProtocol()).thenReturn("HTTP/1.1");
HttpServletResponse response = mock(HttpServletResponse.class);
filter.setForceHttps(true);
FilterChain chain = mock(FilterChain.class);
filter.doFilter(request, response, chain);
verify(chain, never()).doFilter(any(), any());
verify(response, times(1)).sendError(eq(403), anyString());
}
}
None of the above answer are good enough when using Spring Boot and External Tomcat. Here the correct configuration:
super() must be called and the existing dispatcher servlet must be taken from the existing container.
private static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
#Override
public void onStartup(ServletContext container) throws ServletException {
super.onStartup(container);
// Get the existing dispatcher servlet
ServletRegistration.Dynamic dispatcher = (ServletRegistration.Dynamic)
container.getServletRegistration(DISPATCHER_SERVLET_NAME);
// Force HTTPS, and don't specify any roles for this constraint
HttpConstraintElement forceHttpsConstraint =
new HttpConstraintElement(ServletSecurity.TransportGuarantee.CONFIDENTIAL);
ServletSecurityElement securityElement =
new ServletSecurityElement(forceHttpsConstraint);
// Add the security element to the servlet
dispatcher.setServletSecurity(securityElement);
}

Using Guice Servlet with Jetty to map paths to servlets without using a web.xml

I am struggling with getting Guice Servlet working to configure how Jetty serves web requests, in this simple case, for static pages.
I have created a simple application that is supposed to map two different requests, one using GuiceServlet, another not. The latter works, while the GuiceServlet mapped one returns a 404 error.
Any tips? I am using: JDK 1.7.0_15; eclipse.jetty.jetty-servlet 8.1.9.v20130131; guice-servlet 3.0. Thanks.
public class Main {
public static void main(String... args) {
Guice.createInjector().getInstance(Main.class).start();
}
public void start() {
Server server = new Server(8080);
ServletContextHandler handler = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
handler.addEventListener(new MyGuiceServletConfig());
handler.addServlet(MyServlet.class, "/non-guice");
server.setHandler(handler);
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class MyGuiceServletConfig extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule() {
#Override
protected void configureServlets() {
System.out.println("MyGSC->getInjector->configureServlets"); //I'm seeing this in the console...
serve("/guice").with(MyServlet.class);
}
});
}
}
#Singleton
public class MyServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().print("Hello!\nYour path is: " + request.getServletPath());
}
}
Beyond this, what's the best way to create the various injectors? I have the main(..) structured like so, so that I can plug in other Modules, leaving the MyServletModule to be specified in MyGuiceServletConfig as I saw somewhere - is this correct?
I ended up being able to this much simpler, in a way that works. Adding a DefaultServlet for the "/" path was necessary:
public class MyMain {
public static void main(String... args) throws Exception {
Guice.createInjector(new MyServletModule());
Server server = new Server(8080);
ServletContextHandler handler =
new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
handler.addFilter(GuiceFilter.class, "/*", allOf(DispatcherType.class));
handler.addServlet(DefaultServlet.class, "/");
server.start();
}
}
#Singleton
public class MyServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().print("Hello!\nYour path is: " + request.getServletPath());
}
}
public class MyServletModule extends ServletModule {
#Override
protected void configureServlets() {
serve("/guice").with(MyServlet.class);
}
}
If you want Jetty to serve the static content, be sure you configure the DefaultServlet too.
Example found in the Jetty embedded examples tree: OneServletContext.java
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class OneServletContext
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
// Serve static content from /tmp
ServletHolder holder = context.addServlet(DefaultServlet.class,"/tmp/*");
holder.setInitParameter("resourceBase","/tmp");
holder.setInitParameter("pathInfoOnly","true");
// Serve some hello world servlets
context.addServlet(new ServletHolder(new HelloServlet()),"/*");
context.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")),"/it/*");
context.addServlet(new ServletHolder(new HelloServlet("Bonjour le Monde")),"/fr/*");
server.start();
server.join();
}
}
This will serve content from your filesystem directory /tmp as the URL context path of http://localhost:8080/tmp/.
Examples:
File System URL
/tmp/hello.txt http://localhost:8080/tmp/hello.txt
/tmp/a/hi.txt http://localhost:8080/tmp/a/hi.txt
/tmp/index.html http://localhost:8080/tmp/

Categories