Embedded Jetty + Spring MVC main method - spring-mvc

I'm using Embedded Jetty and Spring MVC and i'm starting my WebApplication through the main function.
i don't like the way it's looks, it's not very clean and i have the feeling i'm doing something wrong even though it's working just fine.
my problem is that i want to initialize ServerHolder,ServletContextHandler and Server objects outside the main method, but i cannot do it because DispatcherServlet requires and ApplicationContext.
Any ideas?
public static void main(String[] args) throws Exception {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(EPConfiguration.class);
ServletHolder servletHolder = new ServletHolder(new DispatcherServlet(applicationContext));
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(servletHolder, "/*");
BasicConfigurator.configure();
Server server = new Server();
server.setHandler(context);
//HTTP
ServerConnector connector = new ServerConnector(server);
connector.setPort(9999);
server.setConnectors(new Connector[]{connector});
server.start();
server.join();
}

I'm not familier with Jetty, but you can setup and initialize DispatcherServlet in the following way:
Declare a java class which implements the abstract class AbstractAnnotationConfigDispatcherServletInitializer (it usually will be called WebAppInitializer).
Implement its abstract methods - getRootConfigClasses() (which return the classes which configure Application Context), getServletConfigClasses() (which returns the classes which configure the ServletContext) and getServletMappings() (which sets the DispatcherServletmapping).
I usually call the class that configures the ServletContext as WebConfig. You should annotate it with the #Configuration and #EnableWebMvc. In addition you should use it to define a ViewResolver bean:
```java
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view"):
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
```

Related

spring boot automatic redirect http to https

Good day, I have the application with microservices and gateway (zuul) built on SpringBoot 2. It is all uses SSL.
I need the automatic redirect from: http:\\localhost (currently shows nothing) to https:\\localhost (shows some text), so the user doesn't need to bother.
Once again: http:\\localhost has to show the same text as https:\\localhost (I need a redirect)
I've tried, does nothing.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure();
}
}
Tried another approach, but SpringBoot failed to recognize TomcatEmbeddedServletContainerFactory
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(createHttpConnector());
return tomcat;
}
private Connector createHttpConnector() {
Connector connector
= new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setSecure(false);
connector.setPort(8080);
connector.setRedirectPort(8443);
return connector;
}
this one doesn't work either (doesn't seems to change anything)
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment environment;
#Override
public void configure(HttpSecurity http) throws Exception {
// other security configuration missing
http.portMapper()
.http(80) // http port defined in yml config file
.mapsTo(443); // https port defined in yml config file
// we only need https on /auth
http.requiresChannel()
.antMatchers("/auth/**").requiresSecure()
.anyRequest().requiresInsecure();
}
}
and this one ain't working too, the error is Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
#Bean
public TomcatServletWebServerFactory httpsRedirectConfig() {
return new TomcatServletWebServerFactory () {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
}
and even this one with the java.lang.IllegalStateException: No ServletContext set error
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(80);
connector.setSecure(false);
connector.setRedirectPort(443);
return connector;
}
Any suggestions?
Got it.
Thanks to EstebanGarciaAlonso and his answer ugrade spring boot 2.0.0.RC2 exception No ServletContext set
"After debugging, the problem is that mvc configuration class
EnableWebMvcConfiguration load too early, servlet not loaded yet."
I spent a few hours on this. I managed to find a reason why this was
happening. My config was split into several files and I was creating a
MVC related bean in the Security Config (which was created earlier)
forcing to use the MVC config before its time.
The solution was to move the #Bean instance from the security config
to the MVC config. I hope it helps other people!
I moved following code to Application.java just before main method and all worked like miracle
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(80);
connector.setSecure(false);
connector.setRedirectPort(443);
return connector;
}

Spring MVC InternalResourceViewResolver

I'm a newer of Spring MVC , and I'm trying to config a simple controller like below ,but when i test it. I got
javax.servlet.ServletException: Circular view path [index]: would dispatch back to the current handler URL [/index] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
Here is my WebConfig.java Code:
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
Here is my IndexController.java Code:
#Controller
#RequestMapping("/index")
public class IndexController {
#RequestMapping(method = RequestMethod.GET)
public String index() {
return "index";
}
}
Here is my Test Fiel :
public class TestIndexController {
#Test
public void testIndexController() throws Exception {
IndexController indexController = new IndexController();
MockMvc mockMvc = standaloneSetup(indexController).build();
mockMvc.perform(get("/index")).andExpect(view().name("index"));
}
}
Every time when i changed the get("/index") to get("/index.jsp") ,i passed the test. but i just can't figure it out, please help me out
In your test setup, you create an instance of the Controller under test, but the context created for the test has no knowledge of the existence of your WebConfig and therefore, no instance of your ViewResolver.
Here is a quick fix :
public void testIndexController() throws Exception {
IndexController indexController = new IndexController();
MockMvc mockMvc = standaloneSetup(indexController)
setViewResolvers((new WebConfig()).viewResolver()).build();
mockMvc.perform(get("/index")).andExpect(view().name("index"));
}
If you don't want to access WebConfig in your test class, you can also create a new instance of ViewResolver and add it to your mockMvc setup.
Hope this helps.

How to define isolated web contexts in a modular Spring Boot application?

Given a Spring Boot application comprised of a bootstrap module, and two or more isolated business modules - each of which exposes REST API specific to a business domain, and each of which uses an independent, isolated document store for data persistence, how do I go about configuring such an application, such that:
The bootstrap module defines a parent context (non-web) which provides certain shared resources to underlying modules (global config, object mappers, etc.)
Each business module exposes their REST controllers on the same port but using a different context path. Ideally, I want to be able to define a base path for each module (e.g. /api/catalog, /api/orders) and separately define the sub-path within each controller.
Each business module defines its own repository configuration (e.g. different MongoDB settings for each module)
In order to isolate the contexts of the individual business modules (which allows me to manage independent repository configurations in each module) I have tried using the context hierarchies available in SpringApplicationBuilder to isolate the contexts of each of the individual business modules:
public class Application {
#Configuration
protected static class ParentContext {
}
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(ParentContext.class)
.child(products.config.ModuleConfiguration.class)
.web(true)
.sibling(orders.config.ModuleConfiguration.class)
.web(true)
.run(args);
}
}
however as each module contains a configuration class annotated with #EnableAutoConfiguration this causes Spring Boot to attempt to launch two independent embedded servlet containers, each trying to bind to the same port:
#Configuration
#EnableAutoConfiguration
public class WebApplicationConfiguration {
#Value("${api.basePath:/api}")
protected String apiBasePath;
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(),
apiBasePath + "/products/*");
registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registration;
}
}
The Spring Boot documentation on context hierarchies states that the parent context cannot be a web context, so I'm a bit lost as to how I can share an embedded servlet container between isolated child contexts.
I have created a minimal GitHub project to illustrate the point:
Create configuration class for business module child1's context
package com.child1;
#Configuration
#ComponentScan(basePackages = {"com.child1a", "com.child1b"})
public class Child1Configuration {
}
Create configuration class for business module child2's context
package com.child2;
#Configuration
#ComponentScan(basePackages = {"com.child2a", "com.child2b"})
public class Child2Configuration {
}
Create a configuration class for bootstrap module parent context. Specify the component scanning for beans to be shared by child contexts
package com.parent;
#Configuration
#ComponentScan(basePackages = {"com.parent1", "com.root"})
public class ParentConfiguration {
}
Create SpringBootApplication class with two dispatcher servlets beans, one for each business module. Create application context for each servlet and set the context created by the boot application as root. Basically spring will inject the context into the ApplicationContext parameter of the #Bean methods.
package com.parent;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
}
#Bean
public ServletRegistrationBean child1(ApplicationContext parentContext) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDetectAllHandlerMappings(false);
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.setParent(parentContext);
applicationContext.register(Child1Configuration.class);
applicationContext.refresh();
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new
ServletRegistrationBean(dispatcherServlet, true, "/child1/*");
servletRegistrationBean.setName("child1");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
#Bean
public ServletRegistrationBean child2(ApplicationContext parentContext) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDetectAllHandlerMappings(false);
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.setParent(parentContext);
applicationContext.register(Child2Configuration.class);
applicationContext.refresh();
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new
ServletRegistrationBean(dispatcherServlet, true, "/child2/*");
servletRegistrationBean.setName("child2");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
}

Spring Boot & Thymeleaf with XML Templates

I have a Spring Boot application with a controller that returns a ModelAndView and Thymeleaf to render templates, where the templates live in /src/main/resources/templates/*.html
This works fine, but How can I configure Spring and/or Thymeleaf to look for xml files instead of html?
If it helps, I'm using Gradle with the org.springframework.boot:spring-boot-starter-web dependency to set things up. I am currently running the server using a class with a main method.
After trying and failing at various bean defs for viewResolver and related things, I finally got this working with a change to my application.yaml file:
spring:
thymeleaf:
suffix: .xml
content-type: text/xml
For those reading this later, you can do similar with your application.properties file (with dot notation in place of the yaml indentation).
This works too :
#Configuration
public class MyConfig
{
#Bean
SpringResourceTemplateResolver xmlTemplateResolver(ApplicationContext appCtx) {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(appCtx);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".xml");
templateResolver.setTemplateMode("XML");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCacheable(false);
return templateResolver;
}
#Bean(name="springTemplateEngine")
SpringTemplateEngine templateEngine(ApplicationContext appCtx) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(xmlTemplateResolver(appCtx));
return templateEngine;
}
}
And for the usage
#RestController
#RequestMapping("/v2/")
public class MenuV2Controller {
#Autowired
SpringTemplateEngine springTemplateEngine;
#GetMapping(value ="test",produces = {MediaType.APPLICATION_XML_VALUE})
#ResponseBody
public String test(){
Map<String, String> pinfo = new HashMap<>();
Context context = new Context();
context.setVariable("pinfo", pinfo);
pinfo.put("lastname", "Jordan");
pinfo.put("firstname", "Michael");
pinfo.put("country", "USA");
String content = springTemplateEngine.process("person-details",context);
return content;
}
}
Don't forget the template in resources/templates folder
<?xml version="1.0" encoding="UTF-8"?>
<persons >
<person>
<fname th:text="${pinfo['lastname']}"></fname>
<lname th:text="${pinfo['firstname']}"></lname>
<country th:text="${pinfo['country']}"></country>
</person>
</persons>

How to conduct a lookup of an EJB from a servlet context?

I have a POJO that was instantiated from a servlet. I need to make a lookup of an EJB within this POJO, either CDI or JNDI. My JEE container is TomEE 1.6.0.
My question is this: need the EJB have remote interface? Because if I instantiate it directly from the servlet by #EJB the remote interface does not need...
Just see this simple example which always throws NameNotFoundException.
#Stateless
public class MyEJB
{
public String sayHello()
{
return "Hello";
}
}
The next servlet try yo lookup MyEJB:
#WebServlet("/myServlet")
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID=1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException
{
try
{
Context ctx = new InitialContext();
MyEJB ejb = (MyEJB) ctx.lookup("MyEJB");
System.out.println(ejb.sayHello());
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
}
The line: MyEJB ejb = (MyEJB) ctx.lookup("MyEJB"); always throws NameNotFoundException. But if I use #EJB it work fine. But I need do the lookup in JNDI mode because finally I will instantiate this EJB within a POJO.
So, why fails this lookup ?
This has nothing to do with remote interfaces.
When you declare MyEJB in servlet using #EJB it works, because servlet is container-managed - your TomEE server instantiates servlet object. However declaration of MyEJB in MyPojo (also using #EJB) won't work, because MyPojo is not container-managed - it is created using new MyPojo(), not by TomEE server.
You could for example make MyPojo another EJB (using #Stateless) and inject it using #EJB to the servlet - not by creating new MyPojo().
I can solve this problem by myself by instrospecting the JNDI tree with this simple class:
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
public class JndiInstrospector
{
public static void print()
{
try
{
Context ctx = new InitialContext();
String n = ctx.getNameInNamespace();
_print(n);
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException();
}
}
private static void _print(String name) throws Exception
{
try
{
System.out.println("Name in manespace: "+name);
Context ctx = new InitialContext();
NamingEnumeration<Binding> list = ctx.listBindings(name);
while( list.hasMoreElements() )
{
Binding b = list.nextElement();
String s = b.getName();
_print(name+"/"+s);
}
}
catch(Exception ex)
{
// ignore
}
}
}
Finally, the EJB That I was espected to lookup was found at:
"java:/openejb/local/FacadeBeanLocalBean", where FacadadeBean is the name of my EJB (stateless session bean).

Resources