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.
Related
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.
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));
}
}
I've developed a simple app using Sparkjava. I'm using Intellij, and when I run my tests, or run the app locally, all my resources are files.
However, when I deploy, the whole thing runs as a jar file. As such, I need a way to read my resources off the filesystem or the jar, depending on how the app is launched. The following code gets the job done, but it looks clumsy:
String startedOffFile = new java.io.File(Main.class.getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath())
.getName();
InputStream inputStream;
if(startedOffFile.endsWith(".jar")) {
ClassLoader cl = PropertiesParser.class.getClassLoader();
inputStream = cl.getResourceAsStream("myapp.dev.properties");
} else {
inputStream = new FileInputStream(filename);
}
Is there a cleaner/simpler way?
Have your main create this class to determine if your java executable has defined a config.location parameter or will look on the classpath.
eg. java -Dconfig.location=/here/myapp.dev.properties -jar youapp.jar
public class ApplicationProperties {
public ApplicationProperties() throws IOException {
final Properties properties = new Properties();
String location = getProperty("config.location")
if(location != null) {
properties.load(new FileInputStream(getProperty("config.location", ENV_PROPERTIES_PATH)));
} else {
properties.load(Classname.class.getClassLoader().getResourceAsStream("myapp.dev.properties"));
}
}
public Properties getProperties() {
return properties;
}
}
If you're using Maven with IntelliJ, just put the configuration properties file inside the src/main/resources directory in the module. If you're not using Maven then put the properties file in the root of your source tree (outside any package - like: src/myapp.dev.properties).
After packaging/exporting of the JAR the file will be accessible with new Object().getClass().getClassLoader().getResourceAsStream("myapp.dev.properties") (the new Object()... is used, because in some cases/platforms the static ClassLoader is not defined).
The same classpath is used by the IntelliJ/Eclipse environment, which means there is no need for a special case for loading the files.
If you need to differentiate between development and production properties. You can use Maven profiles for build time packaging or you can load the properties with a variable using the -D switch.
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";
Is there a way to read the content of a file (maven.properties) inside a jar/war file with Java? I need to read the file from disk, when it's not used (in memory). Any advise on how to do this?
Regards,
Johan-Kees
String path = "META-INF/maven/pom.properties";
Properties prop = new Properties();
InputStream in = ClassLoader.getSystemResourceAsStream(path );
try {
prop.load(in);
}
catch (Exception e) {
} finally {
try { in.close(); }
catch (Exception ex){}
}
System.out.println("maven properties " + prop);
One thing first: technically, it's not a file. The JAR / WAR is a file, what you are looking for is an entry within an archive (AKA a resource).
And because it's not a file, you will need to get it as an InputStream
If the JAR / WAR is on the
classpath, you can do SomeClass.class.getResourceAsStream("/path/from/the/jar/to/maven.properties"), where SomeClass is any class inside that JAR / WAR
// these are equivalent:
SomeClass.class.getResourceAsStream("/abc/def");
SomeClass.class.getClassLoader().getResourceAsStream("abc/def");
// note the missing slash in the second version
If not, you will have to read the JAR / WAR like this:
JarFile jarFile = new JarFile(file);
InputStream inputStream =
jarFile.getInputStream(jarFile.getEntry("path/to/maven.properties"));
Now you probably want to load the InputStream into a Properties object:
Properties props = new Properties();
// or: Properties props = System.getProperties();
props.load(inputStream);
Or you can read the InputStream to a String. This is much easier if you use a library like
Apache Commons / IO
String str = IOUtils.toString(inputStream)
Google Guava
String str = CharStreams.toString(new InputStreamReader(inputStream));
This is definitely possible although without knowing your exact situation it's difficult to say specifically.
WAR and JAR files are basically .zip files, so if you have the location of the file containing the .properties file you want you can just open it up using ZipFile and extract the properties.
If it's a JAR file though, there may be an easier way: you could just add it to your classpath and load the properties using something like:
SomeClass.class.getClassLoader().getResourceAsStream("maven.properties");
(assuming the properties file is in the root package)