Springboot app read different path when run local vs package maven - java

So, I am developing an app and trying to use #ImportResource in the main class to refer to a xml file that contain configuration for SOAP services. When I am trying to run/debug in my local it's working just fine, but when I use maven package to build jar, it produce java.io.FileNotFoundException error.
Here is my main class
#SpringBootApplication
#ImportResource(locations = "jaxwsconfig.xml")
public class SoapApplication {
public static void main(String[] args) {
SpringApplication.run(SoapApplication.class, args);
}
#Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new WSSpringServlet(), "/Services");
}
}
Here is some errors I got when I use maven package
IOException parsing XML document from ServletContext resource [/jaxwsconfig.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/jaxwsconfig.xml]
PS: I have a question about the filepath. Where does Java Spring refer to when I don't define absolute path? Like in this case when I just define "jaxwsconfig.xml", where does it refer to?

If your jaxwsconfig.xml file is under the resource folder ie src/main/resource, you can directly give it as,
#ImportResource({"classpath*:applicationContext.xml"})
If suppose the file is present inside a folder inside resource src/main/resource/foldername. you have to give as,
#ImportResource(locations = {"classpath:foldername/application-context.xml"})

Related

SpringApplicationBuilder parent webapp and child without webapp doesn't work

I have a spring boot app with a parent application context and a child application context with some legacy code. I would like to have a webapp in the parent application context, and have the child context be a non webapp.
I have my config and properties in application.yml, that gets loaded in the main class
#ComponentScan(...)
#PropertySource(value = "classpath:application.yml", factory = YamlPropertySourceFactory.class)
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication(
public static void main(String[] args) {
new SpringApplicationBuilder(MyApplication.class)
.profiles("Prod")
.web(WebApplicationType.SERVLET)
.listeners(new MainAppListener()) // logging for some of the events
.child(LegacyServiceConfig.class)
.web(WebApplicationType.NONE)
.listeners(new LegacyAppListener()) // logging for some of the events
.run(args);
}
}
What I've tried so far:
I don't have any web application type config in my application.yml (spring.main.web-application-type). I hoped that the configuration I've specified thru code, in the above snippet would have worked, but it doesn't. Neither of the application contexts start as a webapp, effectively ignoring the config web(WebApplicationType.SERVLET) in the above snippet.
I tried putting spring.main.web-application-type=servlet in my
application.yaml. But that seems to apply this config to both parent
and child application context. And both of them try to start as
webapps, which is not what I want.
How do I get this to work? Any pointers?
As per my knowledge in spring-boot start from main only and the main class is provided by Maven, because I had worked on Maven project. All the properties in the project like JDBC connection, port change you can do it in .properties(extension) type file. And this file can also be in format of .yml also. These file is totally different from main class.
If you make any changes in main or add any extra annotation in main then it will throw an error.
So keep every properties in .properties or .yml file and all configuration component scan in pom.xml file you can have two .properties file also
But main class should be simple like this:
#SpringBootApplication()
public class SpringBootProjectApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootProjectApplication.class, args);
}
}
Hope this will help.

Spring (with Jade) Resources

The Problem
My spring-boot application recently changed routing from host/endpoint to host/middle/endpoint. Since the change, I am running into an issue where the resources are not being found relative to the new url structure. Before, I could reference resources like css stylesheets like link(rel='stylesheet', href='css/style.css'), but now the logger shows an error saying it can't find the resource at /middleman/css/style.css.
From my research, I have found that what I need to do is use a resource handler registry. I have created one (as shown below) but it doesn't seem to be working. I think the problem is that even though I now have the resource registry, I am not referencing resources in the registry. What is the proper way to solve this problem and have all resources point load from the same place regardless of the endpoint? I very well may be missing some obvious piece of SOP
Note: This is all a dumbed down representation of my project in order to give the idea of what is going on without giving unnecessary information.
Project Structure
src
main
java
com.mystuff.cool
configurations
ResourceConfiguration.java
controllers
RoutingController.java
application
Application.java
resources
static
css
footer.css
style.css
images
place1.png
location1.png
spot1.png
favicon.ico
javascripts
layout.js
templates
home.jade
Application Class
#ComponentScan(basePackages = {"my.packages"})
#EnableAutoConfiguration
#EnableSAMLSSO
#Configuration
public class Application
{
public static void main(String[] args)
{
SpringApplication.run(new Object[]{ Application.class, ServiceConfig.class, ResourceConfiguration.class}, args);
}
}
Resource Configuration
#EnableWebMvc
#Configuration
public class ResourceConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
{
configurer.enable();
}
}
Controller
#Controller
public class RoutingController
{
#RequestMapping("/house/home")
public String home(Model model)
{
model.addAttribute("title", "Home is where the heart is");
commonModelTribs(model);
return "home";
}
}
Home Page
doctype html
html
title Place-spedia #{title}
link(rel='icon', href='images/favicon.ico')
link(rel='stylesheet', href='css/style.css')
script(src='javascripts/layout.js')
link(rel='stylesheet', href='css/footer.css')
body
div#footer-icons
a(href='place1')
img#place1(src="images/place1.png")
a(href='location1')
img#location1(src="images/location1.png")
a(href='spot1')
img#spot1(src='images/spot1.png')
If you are using spring boot, you don't need to worry about the resource configuration since you are already configuring the resource directory through the auto configuration. The default behavior for the autoconfiguration is to look within resources/static.
Your issue is with your href values, try inserting a leading forward slash:
link(rel='icon', href='/images/favicon.ico')
link(rel='stylesheet', href='/css/style.css')
script(src='javascripts/layout.js')
link(rel='stylesheet', href='/css/footer.css')
Spring is routing your application to a new relative path, so by putting the leading / in your href attributes, you are telling the router to look absolutely within the static directory instead of relatively from the middle directory.

How to force the import/loading of rest controller programatically

I'm starting a spring boot rest service which may load different packages depending on the distribution. This means sometimes the distribution will contain some jars where certain REST controllers are, sometimes this controllers are not there.
So How I'm able to tell spring-boot where to find the controllers with a configuration files. Now I'm sending this info by annotations forcing me to create a "main" per distribution. I will like to define a unique main that imports the controllers defined in a file. In other words I want to access the #Importannotation manually as is shown in the sniped bellow:
#Configuration
#PropertySource("conf.cfg")
#Import(value = {RestContorller1.class, RestContorller2.class})
#EnableAutoConfiguration
#ConfigurationProperties
#SpringBootApplication
#RestController
#EnableSwagger2
public class Application {
public static void main(String[] args) {
String confFile = Const.DEFAULT_CONFIGURATION_FILE;
if(args.length>0)
confFile= args[0];
System.setProperty("spring.config.name",confFile);
Boolean hasStarted = DataProcessingCore.start(confFile);
if(hasStarted) {
SpringApplication springApp = new SpringApplication(Application.class);
try {
springApp.setDefaultProperties(Utils.createPropertyFiles(confFile));
} catch (IOException e) {
e.printStackTrace();
}
springApp.addInitializers();
springApp.run(args);
}
}
}
If I understand you corretly, your controllers reside in a JAR imported by maven/gradle to your main project/s.
In order to create auto-configuration like spring boot does, that in the same way can be used to import your controllers when the jar is in the classpath, you can tell spring to find your custom configuration on start-up.
I wrote a simple example for that here: Creating your own auto-configuration
In pricipal, you create a spring-boot application (without the spring boot maven plugin!! it is important for the classpath and packaging). and create a file named spring.factories (you can find the actual content in the guide I linked) that tells any spring-boot application that have this jar to load your configuration that may do a #ComponentScan to search for your controllers or set the #Bean manually.
Doing that, you don't have to do #Import and the controllers will be loaded dynamically.

Java Main class overriding properties file at runtime

I am using Apache Maven and Spring. In the src/main/resources folder I have a properties file. These property values can have different values.
I am using PropertyPlaceholderConfigurer.
#Configuration
public class ResourceConfig {
#Bean
public PropertyPlaceholderConfigurer properties( ) {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
ppc.setLocations(new ClassPathResource[] {new ClassPathResource("propertiesFile")});
return ppc;
}
}
I replace these values at runtime:
#Configuration
public class DataSourceConfig {
#Value("${jdbc.url}")
private String jdbcUrlDefault;
}
This is just a sample. I have a main method:
public static void main(String[] args) {
// accept a properties file and replace those values defined in DataSourceConfig class
}
When Apache Maven builds the application the properties file will be on the classpath. The properties file are used during the unit testing. I want to some how replace the properties with a new properties file before the main program is launched for production.
I have seen some example of Properties.load(), but I don't want to do this. I want to accept a properties file through the main program that gets replaced, so the Spring side starts the PropertyPlaceholderConfigurer.
How can this be achieved?
you can place your test properties files in src/test/resources/. In test classes, it will use properties file from this location.
properties file placed here above location will not included in your classpath in final build.
use src/main/resources to place resource files that you want in main program

In a test I can find a xml, in another I can't

I have an interesting problem, I have two web services defined in a spring-conf.xml file and I have two test classes that live in the same package and every class has its own link to this spring-conf.xml file to call their particular webservice.
I am able to get beans from one of my test classes but from the other one I can't and the code is equal in both classes.
In one I have this
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class WSFirstTest {
private ApplicationContext context = new ClassPathXmlApplicationContext(
"WEB-INF/spring-conf.xml");
private WSFirst ws = (WSFirst) context
.getBean("serviceFirstDefault");
in the other one I have this
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class WSSecondTest {
private ApplicationContext context2 = new ClassPathXmlApplicationContext(
"WEB-INF/spring-conf.xml");
private WSSecond ws = (WSSecond) context2
.getBean("serviceSecondDefault");
In the second one my program can't find the spring-conf file, but in the other one is able.
What's the problem?
PS Both wservices work fine when I call them from remote, only in local I have this problem.
The test's error:
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [WEB-INF/spring-conf.xml]; nested exception is java.io.FileNotFoundException: class path resource [WEB-INF/spring-conf.xml] cannot be opened because it does not exist
Thank you for your advises.
As I know Spring interprets an application context path without starting "/" as a relative path from the same package in which the test class is defined and pathes with starting "/" as fully qualified class path locations (at least in the testing environment). Maybe you should try to use a starting "/".
It turned out that different test run configurations caused the problem (see comments below).

Categories