1

UPDATE: I'll still keep Artem Bilan's answer marked as correct, but I still felt I needed to point this out for any future readers. It seems I was misunderstanding the concept of the 'default value' for the @Value annotation. The functionality I was hoping to achieve by using

@Value("${someProp:defaultFilePath}")
private Resource resourcefilePath;

was that, should the file path someProp defined in application.properties throw an exception (i.e. file not found), it would then attempt to use the defaultFilePath (i.e. the one above). What defining a default value actually does is that, if the property itself (someProp) does not exist (not defined or commented out) in the application.properties file, it then attempts to use the default value instead.


I'm using Spring Integration Sftp for SSH file transfer. Using Spring Boot so no xml files. Before configuring the DefaultSftpSessionFactory object, I define a resource which contains a .txt file that has the private key required for the sFtp authentication.

Previously, I used FileSystemResource like this:

Resource resource = new FileSystemResource("C:/absolute/path/to/my/private/key/privateKey.txt");

This worked just fine. However, this application will eventually be put in a cloud environment, which means absolute paths like that won't work anymore. I'm trying to instead use ClassPathResource but it's not working no matter what I try. So far I've tried the following:

Resource resource = new ClassPathResource("privateKey.txt");
Resource resource = new ClassPathResource("privateKey.txt", SomeClassInClassPath.class);
Resource resource = new ClassPathResource("com/my/package/name/privateKey.txt");

My directory structure looks something like this:

ProjectFolder -> src -> main -> java -> com -> my -> package -> name -> various java classes
                                                                        privateKey.txt
                             -> resources -> etc...

There's more but this is a simplified version of it. Can anyone help figure out how I can get it to recognize the path to my .txt? I keep getting java.io.FileNotFoundException: class path resource [resource] cannot be opened because it does not exist no matter what I try.

EDIT: WAR structure:

ProjectFolder -> META-INF -> maven -> etc..
              -> org -> springframework -> boot -> etc..
              -> WEB-INF -> classes -> com
                                    -> public
                                    -> application.properties
                                    -> privateKey.txt
bscott
  • 311
  • 2
  • 5
  • 19
  • Have you tried moving your "privateKey.txt" under the "resources" directory? `resources -> com -> my -> package -> name -> privateKey.txt` – dnault May 12 '16 at 22:12
  • 1. Please check that your final war/jar file contains 'privateKey.txt' in class path. 2 You can use `@Value("classpath:privateKey.txt") Resource privateKey` construction. – Boris Treukhov May 12 '16 at 22:24

1 Answers1

2

You show src -> main -> etc., but that doesn't matter for runtime.

If you really are going to have that file in the final application (jar? war?), be sure that you pack that file really as a part of the final application.

And share that structure here.

With Spring Java & Annotation Configuration you don't need to worry about ClassPathResource object. There is just enough to declare it like this in the appropriate @Configuration:

@Value("com/my/package/name/privateKey.txt")
private Resource privateKey;
Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • I updated my question to show WAR structure. It seems that even after I place privateKey.txt in my classpath (com/my/package/name/privateKey.txt), it ends up being placed in the classes folder in the WAR. Any idea how I would reference that path? – bscott May 13 '16 at 15:35
  • `@Value("WEB-INF/classes/com/my/package/name/privateKey.txt")` ? – Artem Bilan May 13 '16 at 15:44
  • Well, it ended up working with `@Value("WEB-INF/classes/privateKey.txt")`, but this is only working in the cloud environment. Is there a way I can also relatively define the path for this key when I'm working locally as well? No matter what path I try to put in `@Value()` , I get a `java.io.FileNotFoundException: Could not open ServletContext resource [resource]` – bscott May 13 '16 at 21:04
  • Try this `@Value("${my.local.path.to.privateKey:WEB-INF/classes/privateKey.txt}")`, where `my.local.path.to.privateKey` you can specify any possible external way, e.g. as `-D` Java option. – Artem Bilan May 13 '16 at 21:18
  • Sorry, still kind of new to Spring so making sure I'm understanding this correctly. Whenever I use `@Value` with the `${}` syntax, it searches for a property that I've defined in my application.properties file. I went and created a property called privateKeyPath=local.path.to.privateKey and tried what you wrote and I'm still getting `java.io.FileNotFoundException: Could not open ServletContext resource [resource]`. I guess I could just go back to using absolute system filepath with `FileSystemResource` starting from `C:` if all else fails. – bscott May 13 '16 at 21:43
  • M-m-m. try `file:` prefix in your `privateKeyPath` definition. Without any prefix ti tries to resolve resource from the context provider. In your case it is `ServletContext`. So, forcing it into `file:` makes the app to choose `FileSystemResourceLoader`: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html#resources-implementations – Artem Bilan May 13 '16 at 22:13
  • Thanks, that worked! So by making it `@Value(${privateKeyPath:WEB-INF/classes/privateKey.txt})`, is this essentially saying it will attempt to search both places? What I mean is, I won't have to keep manually editing this `@Value` annotation every time i need to switch between my local workstation and the cloud environment right? – bscott May 13 '16 at 22:25
  • Well, the syntax looks like `${properties.key:defaultValue}`. If it can't resolve the first one it falls back to the default value. And only after that it goes to the `Resource` resolution by the final `String`. – Artem Bilan May 13 '16 at 22:28
  • Ok thanks, makes sense. Locally, it should never fall back to the defaultValue path because properties.key should work. When the WAR gets deployed to our cloud server, the path specified in properties.key will obviously not be resolved. It will then check defaultValue, which will resolve successfully. I've gotta take a look at that documentation. But thanks, marking this as correct! – bscott May 13 '16 at 22:35