Spring Boot access static resources missing scr/main/resources - java

I am working on a Spring Boot application. I need to parse an XML file (countries.xml) on start. The problem is that I do not understand where to put it so that I could access it.
My folders structure is
ProjectDirectory/src/main/java
ProjectDirectory/src/main/resources/countries.xml
My first idea was to put it in src/main/resources, but when I try to create File (countries.xml) I get a NPE and the stacktrace shows that my file is looked in the ProjectDirectory (so src/main/resources/ is not added). I tried to create File (resources/countries.xml) and the path would look like ProjectDirectory/resources/countries.xml (so again src/main is not added).
I tried adding this with no result
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addResourceHandlers(registry);
}
I know that I can add src/main/ manually, but I want to understand why is it not working as it has to. I also tried examples with ResourceLoader - with the same no result.
Could anyone suggest what the problem is?
UPDATE:
Just for future references - after building the project, I encountered problem with accessing file, so I changed File to InputStream
InputStream is = new ClassPathResource("countries.xml").getInputStream();

Just use Spring type ClassPathResource.
File file = new ClassPathResource("countries.xml").getFile();
As long as this file is somewhere on classpath Spring will find it. This can be src/main/resources during development and testing. In production, it can be current running directory.
EDIT: This approach doesn't work if file is in fat JAR. In such case you need to use:
InputStream is = new ClassPathResource("countries.xml").getInputStream();

While working with Spring Boot application, it is difficult to get the classpath resources using resource.getFile() when it is deployed as JAR as I faced the same issue.
This scan be resolved using Stream which will find out all the resources which are placed anywhere in classpath.
Below is the code snippet for the same -
ClassPathResource classPathResource = new ClassPathResource("fileName");
InputStream inputStream = classPathResource.getInputStream();
content = IOUtils.toString(inputStream);

To get the files in the classpath :
Resource resource = new ClassPathResource("countries.xml");
File file = resource.getFile();
To read the file onStartup use #PostConstruct:
#Configuration
public class ReadFileOnStartUp {
#PostConstruct
public void afterPropertiesSet() throws Exception {
//Gets the XML file under src/main/resources folder
Resource resource = new ClassPathResource("countries.xml");
File file = resource.getFile();
//Logic to read File.
}
}
Here is a Small example for reading an XML File on Spring Boot App startup.

I use spring boot, so i can simple use:
File file = ResourceUtils.getFile("classpath:myfile.xml");

You need to use following construction
InputStream in = getClass().getResourceAsStream("/yourFile");
Please note that you have to add this slash before your file name.

You can use following code to read file in String from resource folder.
final Resource resource = new ClassPathResource("public.key");
String publicKey = null;
try {
publicKey = new String(Files.readAllBytes(resource.getFile().toPath()), StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}

I use Spring Boot, my solution to the problem was
"src/main/resources/myfile.extension"
Hope it helps someone.

Because java.net.URL is not adequate for handling all kinds of low level resources, Spring introduced org.springframework.core.io.Resource. To access resources, we can use #Value annotation or ResourceLoader class.
#Autowired
private ResourceLoader resourceLoader;
#Override
public void run(String... args) throws Exception {
Resource res = resourceLoader.getResource("classpath:thermopylae.txt");
Map<String, Integer> words = countWords.getWordsCount(res);
for (String key : words.keySet()) {
System.out.println(key + ": " + words.get(key));
}
}

Related

Spring boot images in src main resourcer aren't visible from java code

in a spring boot application, i have an image: src/main/resources/images/logo.jpg
I need to use that image creating new File, but this doesn't work:
File img = new File("/images/logo.jpg");
How can i do this?
Thankss
The resources folder should be in your classpath. This means you can access it like this:
#Service
class SomeClass {
#Value("classpath:images/logo.jpg")
private Resource img;
private void methodThatUsesImage() {
// you can do something here with the InputStream of img
}
}
There are different ways to load resource files, one practice is as follow
ClassLoader cl = getClass().getClassLoader();
File file = new File(cl.getResource("images/logo.jpg").getFile());
And in spring based project you can use
...
ClassPathResource resource = new ClassPathResource("images/logo.jpg");
...
For not facing FileNotFoundException at runtime please see the attached link as well.
new File("/images/logo.jpg");
java will look up in the "working directory/images/logo.jpg". it does not exist, right?
Your file is in src/main/resources so the correct is
URL url = this.getClass().getClassLoader().getResource("/images/logo.jpg");
File file = new File(url.getFile());

No such file or directory when reading Properties File in Java from one Class but not another

I am trying to read a properties folder from this path with respect to the repository root:
rest/src/main/resources/cognito.properties
I have a Class CognitoData from this path: rest/src/main/java/com/bitorb/admin/webapp/security/cognito/CognitoData.java which loads the Properties folder using this code, and it runs fine:
new CognitoProperties().loadProperties("rest/src/main/resources/cognito.properties");
#Slf4j
public class CognitoProperties {
public Properties loadProperties(String fileName) {
Properties cognitoProperties = new Properties();
try {
#Cleanup
FileInputStream fileInputStream = new FileInputStream(fileName);
cognitoProperties.load(fileInputStream);
} catch (IOException e) {
log.error("Error occured. Exception message was [" + e.getMessage() + "]");
}
return cognitoProperties;
}
}
However, when I call CognitoData from a test class located in rest/src/test/java/com/bitorb/admin/webapp/security/cognito/CognitoServiceTest.java , I get this error:
[rest/src/main/resources/cognito.properties (No such file or directory)]
Can anybody shed light on why this is happening?
File directory is not actually relative in that case. You need to provide appropriate file path for this. If you are already using spring boot, then
you can change your code to:
// this will read file from the resource folder.
InputStream inputStream = getClass().getClassLoader()
.getResourceAsStream("cognito.properties");
cognitoProperties.load(inputStream);
Otherwise you need to provide the full absolute path. new CognitoProperties().loadProperties("/absolutepath/..../cognito.properties")
I don't know what you're using for testing, but I suspect that the working directory when you run tests is not the project root.
One solution is to use an absolute path instead:
/absolute/path/to/project/rest/src/main/resources/cognito.properties
Or maybe check what is the working directory during testing and see if it can be changed to the project root.

Reading file inside Spring boot fat jar

We have a spring boot application which has a legacy jar api that we use that needs to load properties by using InputFileStream. We wrapped the legacy jar in our spring boot fat jar and the properties files are under BOOT-INF/classes folder. I could see spring loading all the relevant properties but when I pass the properties file name to the legacy jar it could not read the properties file as its inside the jar and is not under physical path. In this scenario how do we pass the properties file to the legacy jar?
Please note we cannot change the legacy jar.
Actually you can, using FileSystem. You just have to emulate a filesystem on the folder you need to get the file from. For example if you wanted to get file.properties, which is under src/main/resources you could do something like this:
FileSystem fs = FileSystems.newFileSystem(this.getClass().getResource("").toURI(), Collections.emptyMap());
String pathToMyFile = fs.getPath("file.properties").toString();
yourLegacyClassThatNeedsAndAbsoluteFilePath.execute(pathToMyFile)
I also have faced this issue recently. I have a file I put in the resource file, let's call it path and I was not able to read it. #madoke has given one of the solutions using FileSystem. This is another one, here we are assuming we are in a container, and if not, we use the Java 8 features.
#Component
#Log4j2
public class DataInitializer implements
ApplicationListener<ApplicationReadyEvent> {
private YourRepo yourRepo; // this is your DataSource Repo
#Value(“${path.to.file}”). // the path to file MUST start with a forward slash: /iaka/myfile.csv
private String path;
public DataInitializer(YourRepo yourRepo) {
this.yourRepo = yourRepo;
}
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
try {
persistInputData();
log.info("Completed loading data");
} catch (IOException e) {
log.error("Error in reading / parsing CSV", e);
}
}
private void persistInputData() throws IOException {
log.info("The path to Customers: "+ path);
Stream<String> lines;
InputStream inputStream = DataInitializer.class.getClassLoader().getResourceAsStream(path);
if (inputStream != null) { // this means you are inside a fat-jar / container
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
lines = bufferedReader.lines();
} else {
URL resource = this.getClass().getResource(path);
String path = resource.getPath();
lines = Files.lines(Paths.get(path));
}
List<SnapCsvInput> inputs = lines.map(s -> s.split(","))
.skip(1) //the column names
.map(SnapCsvInput::new)
.collect(Collectors.toList());
inputs.forEach(i->log.info(i.toString()));
yourRepo.saveAll(inputs);
}
}
Basically, you can't, because the properties "file" is not a file, it's a resource in a jar file, which is compressed (it's actually a .zip file).
AFAIK, the only way to make this work is to extract the file from the jar, and put it on your server in a well-known location. Then at runtime open that file with an FileInputStream and pass it to the legacy method.
According to #moldovean's solution, you just need to call 'getResourceAsStream(path)' and continue with returned InputStream. Be aware that the 'path' is based on ClassPath. For example:
InputStream stream = this.getClass().getResourceAsStream("/your-file.csv");
In root of your class path, there is a file named 'your-file.csv' that you want to read.

Can't read in pom.xml file from webapp

So my tomcat webapps directory looks like this:
C:/tomcat/webapps/myApp/
myApp/
resources/...
META-INF/
MANIFEST.MF
maven/
my.package.name/
myApp/
pom.properties
pom.xml
WEB-INF/
classes/...
lib/...
web.xml
I have an AppConfig.java (java spring config) where I am trying to get the pom.xml file so I can get certain things out of it. I have tried many things but have been unsuccessful in getting the file. I have a bean that I have just been putting a breakpoint in and trying different things to get the file.
#Bean
public String clientVersion()
{
BufferedReader reader = new BufferedReader(new InputStreamReader(ClassLoader.class.getResourceAsStream("/pom.xml")));
return "";
}
I have tried ClassLoader.class.getResourceAsStream() with many different paths though from what I have been able to find in other posts and forums ClassLoader.class.getResourceAsStream("META-INF/maven/my.package.name/myApp/pom.xml") should work, but I get null no matter what I do. Any suggestions?
To load resource you have to provide full path not only filename. Eg /maven/mypackage/myapp/pom.xml
Try with opening stash.
Change the code to:
#Bean
public String clientVersion()
{
BufferedReader reader = new BufferedReader(new InputStreamReader(ClassLoader.class.getResourceAsStream("/META-INF/maven/my.package.name/myApp/pom.xml")));
return "";
}
Depending on how it's stored on you file system, the my.package.name might need to actually be my/package/name.
Don't use the ClassLoader class, because you're probably picking up the wrong classloader (confusing, right?!).
Instead use my.package.name.MyClass.class.getResourceAsStream("/META-INF/maven/my.package.name/myApp/pom.xml")));, this way you can ensure both files (the class and the pom.xml) are available with the same classloader, since the are in the same archive.
I do this to get it as a String (inside a class called ServerResource.java, so swap in your class name):
InputStream is = ServerResource.class.getResourceAsStream("/META-INF/maven/org.buffalo/platform/pom.xml");
String pom = getStringFromInputStream(is);
if you extract your war/jar, you can confirm the path to the pom (for me it's META-INF/maven/org.buffalo/platform_ws/pom.xml)
Class::getResourceAsStream loads resources from the classpath; in a web application, that means files inside WEB-INF/classes, or inside one of the JAR files inside WEB-INF/lib. Your POM file isn't in either of those places, so it's not on the classpath.
Rather, being somewhere under the WAR root, it's a web resource, rather than a classpath resource. You can load web resources using ServletContext::getResourceAsStream.
Your code should look like:
#Bean
public String clientVersion(ServletContext servletContext) throws IOException {
String pomPath = "/META-INF/maven/my.package.name/myApp/pom.xml";
try (InputStream pomStream = servletContext.getResourceAsStream(pomPath)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(pomStream));
return "";
}
}

How do I reference a file that is placed in the WEB-INF folder when using Arquillian?

I am using maven and the standard directory layout. So I have added a testdata.xml file in the src/test/resources folder, and I also added it as:
.addAsWebInfResource("testdata.xml", "testdata.xml")
in the deployment method, and I have confirmed that it is there. This will make the file appear in /WEB-INF/testdata.xml. Now I need to have a reference to this file in my code and I tried several different getClass().getResourceAsStream(...) and failing again and again so I need some advise now.
I need it for my DBUnit integration test. Is this not possible?
Option A) Use ServletContext.getResourceXXX()
You should have a Aquillarian MockHttpSession and a MockServletContext. E.g.:
#Test
public void myTest()
{
HttpSession session = new MockHttpSession(new MockServletContext());
ServletLifecycle.beginSession(session);
..testCode..
// You can obtain a ServletContext (will actually be a MockServletContext
// implementation):
ServletContext sc = session.getServletContext();
URL url = sc.getResource("/WEB-INF/testdata.xml")
Path resPath = new Path(url);
File resFile = new File(url);
FileReader resRdr = new FileReader(resFile);
etc...
..testCode..
ServletLifecycle.endSession(session);
}
You can create resource files & subdirectories in:
the web module document root - resources are accessible from the browser and from classes
WEB-INF/classes/ - resources are accessible to classes
WEB-INF/lib/*.jar source jar - accessible to classes
WEB-INF/lib/*.jar dedicated resource-only jar - accessible to classes
WEB-INF/ directly within directory - accessible to classes. This is what you are asking for.
In all cases the resource can be accessed via:
URL url = sc.getResource("/<path from web doc root>/<resourceFileName>");
OR
InputStream resIS = sc.getResourceAsStream("/<path from web doc root>/<resourceFileName>");
>
These will be packaged into the WAR file and may be exploded into directories on the deployed app server OR they may stay within the WAR file on the app server. Either way - same behaviour for accessing resources: use ServletContext.getResourceXXX().
Note that as a general principle, (5) the top-level WEB-INF directory itself is intended for use by the server. It is 'polite' not to put your web resources directly in here or create your own directory directly in here. Instead, better to use (2) above.
JEE5 tutorial web modules
JEE6 tutorial web modules
Option B): Use Class.getResourceXXX()
First move the resource out of WEB-INF folder into WEB-INF/classes (or inside a jar WEB-INF/lib/*.jar).
If your test class is:
com.abc.pkg.test.MyTest in file WEB-INF/classes/com/abc/pkg/test/MyTest.class
And your resource file is
WEB-INF/classes/com/abc/pkg/test/resources/testdata.xml (or equivalent in a jar file)
Access File using Relative File Location, via the Java ClassLoader - finds Folders/Jars relative to Classpath:
java.net.URL resFileURL = MyTest.class.getResource("resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTedy.class.getResourceAsStream("resources/testdata.xml");
Access File Using full Package-like Qualification, Using the Java ClassLoader - finds Folders/Jars relative to Classpath:
java.net.URL resFileURL = MyTest.class.getResource("/com/abc/pkg/test/resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTest.class.getResourceAsStream("/com/abc/pkg/test/resources/testdata.xml");
OR
java.net.URL resFileURL = MyTest.class.getClassLoader().getResource("com/abc/pkg/test/resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTest.class.getClassLoader().getResourceAsStream("com/abc/pkg/test/resources/testdata.xml");
Hope that nails it! #B)
The way to access files under WEB-INF is via three methods of ServletContext:
getResource("/WEB-INF/testdata.xml") gives you a URL
getResourceAsStream gives you an input stream
getRealPath gives you the path on disk of the relevant file.
The first two should always work, the third may fail if there is no direct correspondence between resource paths and files on disk, for example if your web application is being run directly from a WAR file rather than an unpacked directory structure.
Today I was struggling with the same requirement and haven't found any full source sample, so here I go with smallest self contained test I could put together:
#RunWith(Arquillian.class)
public class AttachResourceTest {
#Deployment
public static WebArchive createDeployment() {
WebArchive archive = ShrinkWrap.create(WebArchive.class).addPackages(true, "org.apache.commons.io")
.addAsWebInfResource("hello-kitty.png", "classes/hello-kitty.png");
System.out.println(archive.toString(true));
return archive;
}
#Test
public void attachCatTest() {
InputStream stream = getClass().getResourceAsStream("/hello-kitty.png");
byte[] bytes = null;
try {
bytes = IOUtils.toByteArray(stream);
} catch (IOException e) {
e.printStackTrace();
}
Assert.assertNotNull(bytes);
}
}
In your project hello-kitty.png file goes to src/test/resources. In the test archive it is packed into the /WEB-INF/classes folder which is on classpath and therefore you can load it with the same class loader the container used for your test scenario.
IOUtils is from apache commons-io.
Additional Note:
One thing that got me to scratch my head was related to spaces in path to my server and the way getResourceAsStream() escapes such special characters: sysLoader.getResource() problem in java
Add this class to your project:
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
public class Init {
private static final String WEB_INF_DIR_NAME="WEB-INF";
private static String web_inf_path;
public static String getWebInfPath() throws UnsupportedEncodingException {
if (web_inf_path == null) {
web_inf_path = URLDecoder.decode(Init.class.getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF8");
web_inf_path=web_inf_path.substring(0,web_inf_path.lastIndexOf(WEB_INF_DIR_NAME)+WEB_INF_DIR_NAME.length());
}
return web_inf_path;
}
}
Now wherever you want to get the full path of the file "testdata.xml" use this or similar code:
String testdata_file_location = Init.getWebInfPath() + "/testdata.xml";

Categories