spring webapp without web.xml and without any spring xml config file - spring-mvc

below is a piece of code taken from
https://github.com/spring-projects/spring-webflow-samples/blob/master/booking-mvc/src/main/java/org/springframework/webflow/samples/booking/config/DispatcherServletInitializer.java
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {
SecurityConfig.class,
DataAccessConfig.class,
WebMvcConfig.class,
WebFlowConfig.class
};
}
....
}
There is no web.xml in this project. There are no spring xml config files, so also no context:component-scan xml tag.
How does spring know, that it should execute getRootConfigClasses from DispatcherServletInitializer ? Does spring scan for classes implementing WebApplicationInitializer ? If so, where is it configured to scan for such classes ?
Is there some default web-fragment.xml in META-INF in some spring jar ? If so in which jar is it ? In spring-web-4.0.5.release.jar there is only < name > spring_web < name > < distributable/> in spring-webmvc.jar there is no web-fragment.xml.

Related

spring boot - How to load context configuration file

I am trying to convert spring mvc app to spring boot. I used to deploy this application in tomcat and test. Now with spring boot I am trying to do the same thing but I am facing issues to load xml file configuration.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Data sources -->
<Environment name="/source/schema" value="${schema}" type="java.lang.String" />
<Resource auth="Container" driverClass="org.postgresql.Driver"
factory="org.apache.naming.factory.BeanFactory"
idleConnectionTestPeriod="30" jdbcUrl="${url}"
maxAdministrativeTaskTime="0" maxConnectionAge="30" maxIdleTime="9" maxPoolSize="3" minPoolSize="2"
name="/source/DataSource" password="${password}"
preferredTestQuery="select 1" testConnectionOnCheckout="true" type="com.mchange.v2.c3p0.ComboPooledDataSource" user="${user}"/>
</Context>
This is my configuration file which I am trying to load. When I put
#ImportResource({"classpath:applicationContext.xml", "classpath:context.xml"})
I am able to load all the bean configuration from applicationcontext.xml but while loading context.xml it is giving
Caused by: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'Context'.
How should I load these entries when deploying spring boot app in tomcat?
By default, JNDI is disabled in embedded Tomcat. You need to call Tomcat.enableNaming() to enable it.
If you can live by Java config,you can try below snippet to add JNDI and other configurations from context.xml using the java config.
#Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatEmbeddedServletContainer(tomcat);
}
};
}
Example :
#Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatEmbeddedServletContainer(tomcat);
}
#Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setName("jdbc/myDataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", "your.db.Driver");
resource.setProperty("url", "jdbc:yourDb");
context.getNamingResources().addResource(resource);
}
};
}
#Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("java:comp/env/jdbc/myDataSource");
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet();
return (DataSource)bean.getObject();
}
Have a look at this github link for related sample
context.xml should go into the /META-INF/ directory in your war files. It is instructions to the Tomcat server, there's no need to configure anything in Spring to try to load it.

Using CXF with Spring Boot Actuator

I am working on a web service host application in which using cxf with spring boot. when I register cxf servlet with following code web service side works and I can see published wsdls.
However after setting cxf servlet Spring boot actuator and rest endpoints not working and returning 404. How can I solve this issue ?
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}
Although I dont know the reason, when I set a name like below it starts working.
#Bean
public ServletRegistrationBean cxfServlet() {
ServletRegistrationBean cxf = new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
cxf.setName("cxfServlet");
return cxf;
}
Here is simple spring boot configuration I use.
#Configuration
#Import(value = { JaxRsConfig.class })
public class CxfRestConfig {
#Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new CXFServlet(), "/cxf/*");
}
#Component
public class CustomSpringComponentScanServer
extends AbstractSpringComponentScanServer {
#Override
protected String getAddress() {
return "/api";
}
#Bean
public Server jaxRsServer() {
super.getFeatures().add(new LoggingFeature());
return super.createJaxRsServer();
}
}
}
Note: With ComponentScanner you need to annotate your service class with Spring annotations along with #Path Annotation at class level.
If you do not want list of apis in http://localhost:8080/cxf you can directly remove the custom class I had written and you can import directly as shown below.
#Import(value = { JaxRsConfig.class, SpringComponentScanServer.class })
I was getting the same problem with Kotlin and this post indirectly helped me. My code was like this
#Bean
fun dispatcherServlet(): ServletRegistrationBean<CXFServlet>? {
return ServletRegistrationBean(CXFServlet(), "/*")
}
After changing the method name from dispatcherServlet to cxfServlet the actuator magically started to work.
#Bean
fun cxfServlet(): ServletRegistrationBean<CXFServlet>? {
return ServletRegistrationBean(CXFServlet(), "/*")
}
I guess it was conflicting with some Spring default servlet.
I looks like there is a clash between servlets.
You can check it in your logs. There should be:
2017-04-01 15:34:04,029 [restartedMain] INFO o.s.b.w.s.ServletRegistrationBean - Mapping servlet: 'CXFServlet' to [/soap-api/*]
2017-04-01 15:34:04,031 [restartedMain] INFO o.s.b.w.s.ServletRegistrationBean - Mapping servlet: 'dispatcherServlet' to [/]
There should be exactly two servlets and the path should be different.
If there is one missing the enpoints won't work.
dispatcherServlet is spring default one to handle actuator metrics

Spring boot app using Grails GSP boot plugin Servlet/Configuration issues

I'm trying to convert a Grails 2 app into a couple of Spring Boot apps, with the re-use for now with all the GSPs. Nice boot plugin created by Lari Hotari & Graeme Rocher
https://github.com/grails/grails-boot
I'm trying to see a way that I can use content negoeation I followed a useful spring blog on the topic and I've been looking into the GspAutoConfiguration See link for more info
Doesn't appear to be a simple way that I can see to still use the GSP Template Engine from the GSP configuration to confgure content negotiation in the MvcConfig in the configureContentNegotiation bean
Gradle.build
def grailsVersion = '2.4.4'
compile "org.grails:grails-gsp-spring-boot:1.0.0"
compile "org.grails:grails-web-gsp:$grailsVersion"
compile "org.grails:grails-web-gsp-taglib:$grailsVersion"
compile "org.grails:grails-web-jsp:$grailsVersion"
compile("javax.servlet.jsp:javax.servlet.jsp-api:2.3.1")
//ensures that the embedded servlet container doesn’t interfere with the servlet container to which the war file will be deployed
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat:1.2.3.RELEASE'
(snipped ...)
Spring MVC Configuration
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
(snippet...)
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false)
.favorParameter(true)
.parameterName("format")
.ignoreAcceptHeader(true)
.useJaf(false)
.defaultContentType(MediaType.TEXT_HTML)
.mediaType("json", MediaType.APPLICATION_JSON)
}
/**
* Create the CNVR. Specify the view resolvers to use explicitly. Get Spring to inject
* the ContentNegotiationManager created by the configurer (see previous method).
*/
#Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
// Define the view resolvers
List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
//NOT this simple due to the way GspAutoConfiguration ovverrides so much view based behaviour
InternalResourceViewResolver r2 = new InternalResourceViewResolver()
r2.setPrefix("/templates/views")
r2.setSuffix(".gsp")
resolvers.add(r2)
JsonViewResolver r1 = new JsonViewResolver()
resolvers.add(r1)
// Create the CNVR plugging in the resolvers and the content-negotiation manager
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setViewResolvers(resolvers);
resolver.setContentNegotiationManager(manager);
return resolver;
}
}

Serving Static resources from file system | Spring Boot Web

Using a Spring Boot web application I trying to serve my static resource from a file system folder outside my project.
Folder structure looks like:-
src
main
java
resources
test
java
resources
pom.xml
ext-resources (I want to keep my static resources here)
test.js
Spring Configuration:-
#SpringBootApplication
public class DemoStaticresourceApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(DemoStaticresourceApplication.class, args);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/test/**").addResourceLocations("file:///./ext-resources/")
.setCachePeriod(0);
}
}
Hitting 'http://localhost:9999/test/test.js' in my browser gives back a 404.
How should I configure ResourceHandlerRegistry to serve static resources from the above mentioned 'ext-resources' folder?
I should be able to switch cache on/off for dev/prod environment.
Thanks
UPDATE 1
Giving absolute file path works:-
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/test/**")
.addResourceLocations(
"file:///C:/Sambhav/Installations/workspace/demo-staticresource/ext-resources/")
.setCachePeriod(0);
}
How can I provide relative location? Absolute path will make my life tough during build & deploy process.
file:/// is an absolute URL pointing to the root of the filesystem and, therefore, file:///./ext-resources/ means that Spring Boot is looking for resources in a directory named ext-resources in the root.
Update your configuration to use something like file:ext-resources/ as the URL.
This is what I did in the WebConfig class, inside the addResourceHandlers method:
boolean devMode = this.env.acceptsProfiles("development");
String location;
if (devMode) {
String currentPath = new File(".").getAbsolutePath();
location = "file:///" + currentPath + "/client/src/";
} else {
location = "classpath:static/";
}
Spring Boot Maven Plugin can add extra directories to the classpath. In your case you could include that in your pom.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<folders>
<folder>${project.build.directory}/../ext-resources</folder>
</folders>
...
</configuration>
</plugin>
So that way you don't need inlcude any hard-code in your classes. Simply start your webapp with
mvn spring-boot:run
static resources (eg:html js css etc) can be placed in the same level directory of project or jar, named public. contents will be servered without additional config.

Importing spring.ftl using Spring MVC, Sitemesh, Freemarker

How can I import the spring.ftl macros into a Freemarker template page using Spring MVC, Sitemesh, and Freemarker?
I've configured a Spring MVC app using Sitemesh and Freemarker based on Ted Young's configuration example. According to the Spring MVC/Freemarker integration reference, it is necessary to import the spring.ftl macros in order to bind the backing model to the view via <#spring.bind "command.name"/>. However, doing this:
<#import "/spring.ftl" as spring>
<#spring.bind "command.user"/>
Results in this exception:
org.springframework.web.util.NestedServletException:
Request processing failed; nested exception is freemarker.
template.TemplateException: Error reading imported file spring.ftl
Others have experienced this issue, but I've yet to find a solution in google land. I also attempted to use this technique (zipping up spring.ftl, placing it in META-INF/lib, and adding the zip to the build path), but it didn't seem to work out.
Thanks!
The problem is that spring dont know where to look after the spring.ftl file:
This is my custom configuration for an MVC project using Boot
/**
* Otras configuraciones de la aplicaciones web, incluyendo algunas definidas en
* xml. Usar #ImportResource("classpath:/extra-config.xml") en caso de quererse
* importar configuracion en xml
*/
#Configuration
#PropertySource("classpath:application.properties")
public class WebAppConfig
{
#Autowired
private ServletContext context;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer placeHolderConfigurer = new PropertySourcesPlaceholderConfigurer();
return placeHolderConfigurer;
}
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() throws IOException, TemplateException
{
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer()
{
#Override
protected void postProcessConfiguration(freemarker.template.Configuration config) throws IOException, TemplateException
{
WebappTemplateLoader WebAppTplLoader = new WebappTemplateLoader(context, "/WEB-INF/ftl");
ClassTemplateLoader classTplLoader = new ClassTemplateLoader(context.getClassLoader(), "/templates");
ClassTemplateLoader baseMvcTplLoader = new ClassTemplateLoader(FreeMarkerConfigurer.class, "");
MultiTemplateLoader mtl = new MultiTemplateLoader(new TemplateLoader[]
{
WebAppTplLoader,
classTplLoader,
baseMvcTplLoader
});
config.setTemplateLoader(mtl);
}
};
configurer.setDefaultEncoding("UTF-8");
configurer.setPreferFileSystemAccess(false);
return configurer;
}
#Bean
public FreeMarkerViewResolver viewResolver()
{
FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
viewResolver.setExposeSpringMacroHelpers(true);
viewResolver.setExposeRequestAttributes(true);
viewResolver.setPrefix("");
viewResolver.setSuffix(".ftl");
viewResolver.setContentType("text/html;charset=UTF-8");
return viewResolver;
}
}
The first 2 loaders allow to load .ftl templates in war files from "/WEB-INF/ftl" and from regular jar files from src/resources/templates.
If you want to use security tags in freemarker the escense are this two lines:
viewResolver.setExposeSpringMacroHelpers(true);
viewResolver.setExposeRequestAttributes(true);
And the baseMvcTplLoader loader to get the spring.ftl from org.springframework.web.servlet.view.freemarker. I advice to explore ftl templates in some example project or documentation to have a clue of how spring.ftl works.
The configuration of the placeholder is not related to the freemarker
configuration, yet its very useful for injecting values in variables
from src/resources/application.properties by using the #Value
annotation.
With this you can use all the spring power within freemarker templates.
I like my spring.ftl included by default without having to add it manually within each view. In your configuration.
Define your freemarkerConfigurer as such.
#Bean(name = "freemarkerConfig")
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/views/");
Map<String, Object> map = new HashMap<>();
map.put("xml_escape", new XmlEscape());
configurer.setFreemarkerVariables(map)
def settings = new Properties()
settings['auto_import'] = 'spring.ftl as spring, layout/application.ftl as l'
configurer.setFreemarkerSettings(settings)
println "returning freemarker config"
return configurer;
}
<#import "spring.ftl" as spring/>
Without /

Resources