all, I am puzzled about the struts2 action unit test
import org.apache.struts2.StrutsSpringTestCase;
import org.junit.Test;
import com.opensymphony.xwork2.ActionProxy;
public class TestLoginAction extends StrutsSpringTestCase {
#Test
public void testCheck() throws Exception {
ActionProxy proxy = null;
LoginAction test = null;
request.setParameter("username", "admin");
proxy = getActionProxy("/checkLogin");
test = (LoginAction) proxy.getAction();
String result = proxy.execute();
assertEquals("error", result);
assertEquals("admin", test.getUsername());
}
}
It throw the warnings and exceptions:
Couldn't get resource paths for class path resource [WEB-INF/jsp/]
java.io.FileNotFoundException: class path resource [WEB-INF/jsp/] cannot be resolved to URL because it does not exist
StrutsSpringTestCase is expecting the Spring configuration to be loaded from classpath:applicationContext.xml. You can override this behavior by adding this to your test action:
#Override
public String getContextLocations() {
return "path/to/your/TestLoginAction-context.xml";
}
the reason of throwing the exception has been found,
I use struts-convention to find my action classes,but
,this configuration is the base search path of jsp files,so of course the convention can't recognize the path as java class path,I will provide the two workaround here:
you can modify the configuration value "/WEB-INF/jsp" to the existing class path,such as "com.foo.bar" to make the convention resolve class path smoothly
rewrite the MockServletContext and swallow the throwing Exception
public Set getResourcePaths(String path) {
....
}
You may add a line as follows to struts.xml to work around it.
<constant name="struts.convention.result.path" value="/your/path" />
Related
Main question:
Could someone advise how I use Spring to retrieve a value in my Java code to get values out of whatever properties file is specified by the context:property-placeholder tag in the applicationContext.xml?
Detailed description:
I am very new to Spring. Trying to use it to retreive configurable properties for my application to connect to a (configurable) JMS queue.
I have a Java/J2EE web application, using Spring.
In the src/main/resources/applicationContext.xml I have the following line:
<context:property-placeholder location="classpath:myapp.properties" />
Then in the src/main/resources/myapp.properties file I have the following lines:
myservice.url=tcp://someservice:4002
myservice.queue=myqueue.service.txt.v1.q
The problem that I am having, is that, for the life of me, I cannot figure out how to get the value of myservice.url that is defined in myapp.properties into my running java code.
I have tried a static function [to be called around the application]:
public static String getProperty(String propName)
{
WebApplicationContext ctx =
FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
Environment env = ctx.getEnvironment();
retVal = env.getProperty(propName);
return retVal;
}
However, while it returns a populated Environment object "env", the env.getProperty(propName) method returns null.
Ok - many, many thanks to Rustam who gave me the clues I needed. The way that I resolved this was thus:
In the src/main/resources/applicationContext.xml I have the following line:
<context:property-placeholder location="classpath:myapp.properties" />
Then in the src/main/resources/myapp.properties file I have the following lines:
myservice.url=tcp://someservice:4002
myservice.queue=myqueue.service.txt.v1.q
Then I have a class as follows:
package my.app.util;
import org.springframework.beans.factory.annotation.Value;
public class ConfigInformation
{
public ConfigInformation()
{
// Empty constructor needed to instantiate beans
}
#Value("${myservice.url}")
private String _myServiceUrl;
public String getMyServiceUrl()
{
return _myServiceUrl;
}
#Value("${myservice.queue}")
private String _myServiceQueue;
public String getMyServiceQueue()
{
return _myServiceQueue;
}
}
Then, I have the following declaration in my applicationContext.xml:
<bean name="configInformation" class="my.app.util.ConfigInformation">
</bean>
After I have done this, I am able to use the following lines in my code:
WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
ConfigInformation configInfo = (ConfigInformation) ctx.getBean("configInformation");
And consequently, the configInfo object, is populated with the values assigned to "myservice.url" and "myservice.queue" which can be retrieved with the method calls:
configInfo.getMyServiceUrl()
and
configInfo.getMyServiceQueue()
I have the following class which I want to unit test:
class PropsProvider {
String propsfileLocation;
void init() {
Path path = getPropsPath();
loadPropertiesFromPath(path);
}
private Path getPropsPath() throws URISyntaxException {
URI fileUri = new URI(propsfileLocation);
return Paths.get(fileUri); // << failure here
}
// ...
}
I am unit testing the above class and I keep getting a failure on the line Paths.get(fileUri). I get an IllegalArgumentException. I am passing in a relative path from my unit test, which is src/test/resources/app.properties. I believe this is happening because I haven't included the prefix file:. The unit test resides in the same package as the above class albeit in the /src/test/java folder. How can I fix this?
You can get the path of any resource using Google's Guava library.
URL myFileLocation=Resources.getResource("myFile.txt");
For more look here.
http://google.github.io/guava/releases/22.0/api/docs/com/google/common/io/Resources.html#getResource-java.lang.Class-java.lang.String-
Is there a way to remove a folder from the classpath similar to adding a folder at runtime (Can a directory be added to the class path at runtime?)
Please find below a snippet as technical example to demonstrate adding / removing a path.
create following source files in any directory
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Stack;
import sun.misc.URLClassPath;
public class EvilPathDemo {
public static void addPath(String path) throws Exception {
URL u = new File(path).toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader)
ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL",
new Class[]{URL.class}
);
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
}
public static void removePath(String path) throws Exception {
URL url = new File(path).toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader)
ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Field ucpField = urlClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
Class<?> ucpClass = URLClassPath.class;
Field urlsField = ucpClass.getDeclaredField("urls");
urlsField.setAccessible(true);
Stack urls = (Stack) urlsField.get(ucp);
urls.remove(url);
}
public static void main(String[] args) throws Exception {
String parm = args.length == 1 ? args[0] : "";
String evilPath = "/tmp";
String classpath = System.getProperty("java.class.path");
boolean isEvilPathSet = false;
for (String path : classpath.split(File.pathSeparator)) {
if (path.equalsIgnoreCase(evilPath)) {
System.out.printf("evil path '%s' in classpath%n", evilPath);
isEvilPathSet = true;
break;
}
}
if (isEvilPathSet && parm.equalsIgnoreCase("REMOVE")) {
System.out.printf("evil path '%s' will be removed%n", evilPath);
removePath(evilPath);
}
tryToLoad("Foo");
if (parm.equalsIgnoreCase("ADD")) {
System.out.printf("evil path '%s' will be added%n", evilPath);
addPath(evilPath);
}
tryToLoad("Bar");
}
private static void tryToLoad(String className) {
try {
Class<?> foo = Class.forName(className);
System.out.printf("class loaded: %s%n", foo.getName());
} catch (ClassNotFoundException ex) {
System.out.println(ex);
}
}
}
.
public class Foo {
static {
System.out.println("I'm foo...");
}
}
.
public class Bar {
static {
System.out.println("I'm bar...");
}
}
compile them as follow
javac EvilPathDemo.java
javac -d /tmp Foo.java Bar.java
During the test we will try to load the classes Foo and Bar.
without /tmp in the classpath
java -cp . EvilPathDemo
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
adding /tmp to the classpath
java -cp . EvilPathDemo add
java.lang.ClassNotFoundException: Foo
evil path '/tmp' will be added
I'm bar...
class loaded: Bar
with /tmp in the classpath
java -cp .:/tmp EvilPathDemo
evil path '/tmp' in the classpath
I'm foo...
class loaded: Foo
I'm bar...
class loaded: Bar
remove /tmp from the classpath
java -cp .:/tmp EvilPathDemo remove
evil path '/tmp' in the classpath
evil path '/tmp' will be removed
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
During the testing I found out that following cases are not working.
addPath(evilPath);
tryToLoad("Foo");
removePath(evilPath); // had not effect
tryToLoad("Bar");
removePath(evilPath);
tryToLoad("Foo");
addPath(evilPath); // had no effect
tryToLoad("Bar");
tryToLoad("Foo");
removePath(evilPath); // had no effect
tryToLoad("Bar");
I did not spent time to find out why. Because I don't see any practical use in it. If you really need/wish to play with the classpaths have a look how classloaders are working.
The removePath method from above did not work for me and my Weld Container, the url stack was always emtpy.
The following ugly smugly method worked:
public static void removeLastClasspathEntry() throws Exception {
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Field ucpField = urlClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
Field loadersField = URLClassPath.class.getDeclaredField("loaders");
loadersField.setAccessible(true);
List jarEntries = (List) loadersField.get(ucp);
jarEntries.remove(jarEntries.size() - 1);
Field pathField = URLClassPath.class.getDeclaredField("path");
pathField.setAccessible(true);
List pathList = (List) pathField.get(ucp);
URL jarUrl = (URL) pathList.get(pathList.size() - 1);
String jarName = jarUrl.toString();
pathList.remove(pathList.size() - 1);
Field lmapField = URLClassPath.class.getDeclaredField("lmap");
lmapField.setAccessible(true);
Map lmapMap = (Map) lmapField.get(ucp);
lmapMap.remove(jarName.replaceFirst("file:/", "file:///"));
}
Class loaders can be nested so instead of modifying the system class loader which is the root of the tree of class loaders, it is better to simply create a nested classloader and use that to load classes.
The system classloader itself is immutable (for good reasons) but you can do whatever you want in nested class loaders, including destroying them to unload classes and resources. This is commonly used in e.g. osgi and application servers to load/unload e.g. plugins, applications, etc.
For nested class loaders you can completely customize how to load classes. The URLClassloader is probably a good starting point for what you want.
I dont think there is a straight forward way to do it. You can follow :
Get class path variables using : System.getenv("CLASSPATH"). It will return semi colon separated values.
String classPath = System.getenv("CLASSPATH")
Take the folder path as input and replace it with "" like :
String remainigPath = classPath.replace(inputpath,"");
Put the remaining paths in an array using split method.
String[] paths = remainigPath .split(";");
For adding classPath, You already have the code.
I had the same issue, so I tackled it by creating a library that works on every ClassLoader that uses a URLClassPath (so, currently, URLClassLoader).
The library has methods for:
Adding new entries in front
Appending new entries
Remove existing entries
Please note that, since this library accesses internal and proprietary APIs, it is no guaranteed to work in future versions of the JDK. It currently does for Java 7 and Java 8 (Oracle and OpenJDK).
Here is the GitHub page (contribution is appreciated), and here is the Maven Central artifact
I'm trying to read deployment specific information from a properties file in my wildfly configuration folder. I tried this:
#Singleton
#Startup
public class DeploymentConfiguration {
protected Properties props;
#PostConstruct
public void readConfig() {
props = new Properties();
try {
props.load(getClass().getClassLoader().getResourceAsStream("my.properties"));
} catch (IOException e) {
// ... whatever
}
}
But apparently this is not working since the configuration folder is not in the classpath anymore. Now I can't find an easy way to do it. My favorite would be something like this:
#InjectProperties("my.properties")
protected Properties props;
The only solution I found on the web so far involves making my own OSGi module, but I believe there must be an easier way to do it (one without OSGi!). Can anyone show me how?
If you want to explicitly read a file from the configuration directory (e.g. $WILDFLY_HOME/standalone/configuration or domain/configuration) there's a system property with the path in it. Simply do System.getProperty("jboss.server.config.dir"); and append your file name to that to get the file.
You wouldn't read it as a resource though, so...
String fileName = System.getProperty("jboss.server.config.dir") + "/my.properties";
try(FileInputStream fis = new FileInputStream(fileName)) {
properties.load(fis);
}
Then the file would be loaded for you.
Also, since WildFly doesn't ship with OSGi support anymore, I don't know how creating an OSGi module would help you here.
Here is a full example using just CDI, taken from this site.
Create and populate a properties file inside the WildFly configuration folder
$ echo 'docs.dir=/var/documents' >> .standalone/configuration/application.properties
Add a system property to the WildFly configuration file.
$ ./bin/jboss-cli.sh --connect
[standalone#localhost:9990 /] /system-property=application.properties:add(value=${jboss.server.config.dir}/application.properties)
This will add the following to your server configuration file (standalone.xml or domain.xml):
<system-properties>
<property name="application.properties" value="${jboss.server.config.dir}/application.properties"/>
</system-properties>
Create the singleton session bean that loads and stores the application wide properties
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
#Singleton
public class PropertyFileResolver {
private Logger logger = Logger.getLogger(PropertyFileResolver.class);
private String properties = new HashMap<>();
#PostConstruct
private void init() throws IOException {
//matches the property name as defined in the system-properties element in WildFly
String propertyFile = System.getProperty("application.properties");
File file = new File(propertyFile);
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
logger.error("Unable to load properties file", e);
}
HashMap hashMap = new HashMap<>(properties);
this.properties.putAll(hashMap);
}
public String getProperty(String key) {
return properties.get(key);
}
}
Create the CDI Qualifier. We will use this annotation on the Java variables we wish to inject into.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
public #interface ApplicationProperty {
// no default meaning a value is mandatory
#Nonbinding
String name();
}
Create the producer method; this generates the object to be injected
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
public class ApplicaitonPropertyProducer {
#Inject
private PropertyFileResolver fileResolver;
#Produces
#ApplicationProperty(name = "")
public String getPropertyAsString(InjectionPoint injectionPoint) {
String propertyName = injectionPoint.getAnnotated().getAnnotation(ApplicationProperty.class).name();
String value = fileResolver.getProperty(propertyName);
if (value == null || propertyName.trim().length() == 0) {
throw new IllegalArgumentException("No property found with name " + value);
}
return value;
}
#Produces
#ApplicationProperty(name="")
public Integer getPropertyAsInteger(InjectionPoint injectionPoint) {
String value = getPropertyAsString(injectionPoint);
return value == null ? null : Integer.valueOf(value);
}
}
Lastly inject the property into one of your CDI beans
import javax.ejb.Stateless;
import javax.inject.Inject;
#Stateless
public class MySimpleEJB {
#Inject
#ApplicationProperty(name = "docs.dir")
private String myProperty;
public String getProperty() {
return myProperty;
}
}
The simplest thing you can do is to run standalone.sh with a -P option referencing your properties file (you need a URL file:/path/to/my.properties, or put the file in $WILDFLY_HOME/bin).
Then all properties from the file will be loaded as system properties.
For injecting configuration properties into your application classes, have a look at DeltaSpike Configuration, which supports different property sources like system properties, environment variables, JNDI entries and hides the specific source from your application.
Alternatively, to avoid setting system properties (which will be global in the sense of being visible to all applications deployed to your WildFly instance), you can also define a custom property source for DeltaSpike reading a properties file from any given location, and these properties will be local to your application.
It sounds like the problem you are trying to solve is managing different (but probably similar) configuration files for running your application in different environments (ie, Production, QA, or even different customers). If that is the case, take a look at Jfig http://jfig.sourceforge.net/ . It would obviate the need for storing property files outside your classpath (but you still could).
What is needed is a hierarchical approach to configuration files. The ninety percent of configuration values that do not change can be maintained in a base file. The other ten percent (or less) may be maintained in their own distinct configuration file. At run time, the files are layered on top of each other to provide a flexible, manageable configuration. For example, in a development environment myhost.config.xml combines with dev.config.xml and base.config.xml to form my unique configuration.
Each configuration file may then be maintained in version control as they have unique names. Only the base files need to be modified when base values change, and it is easy to see the difference between versions. Another major benefit is that changes to the base configuration file will be exhaustively tested before deployment.
InputStream in = null;
File confDir = new File(System.getProperty("jboss.server.config.dir"));
File fileProp = new File(confDir, "my.properties");
try{
//teste fileProp.exists etc.
in = new FileInputStream(fileProp);
Properties properties = new Properties();
properties.load(in);
//You should throws or handle FileNotFoundException and IOException
}finally{
try{
in.close();
}catch(Exception ignored){
}
}
To avoid this kind of problem the issue is to set the jboss.server.config.dir in VM arguments like that :
-Djboss.server.config.dir="[jboss_repository]/server/[default-all-standard-standalone]/conf" –server
If you have in standalone.xml property:
<property name="my.properties" value="propertyValue"/>
you can wasily read it with:
static final String MY_PROPERTY = System.getProperty("my.properties");
Or if you specify context param in web.xml like:
<context-param>
<param-name>MyProperty</param-name>
<param-value>MyPropertyValue</param-value>
</context-param>
You can read it in Java bean:
String myProperty= getServletContext().getInitParameter("MyProperty");
I am using the following method to get a resource from WAR file in WildFly:
this.getClass().getResource(relativePath)
It works when the application is deployed as exploded WAR. It used to work with compressed WAR, too. Yesterday, I did a clean and rebuild of project in Eclipse, and it just stopped working.
When I check the resource root:
logger.info(this.getClass().getResource("/").toExternalForm());
I get this:
file:/C:/JBoss/wildfly8.1.0.CR1/modules/system/layers/base/org/jboss/as/ejb3/main/timers/
So, no wonder it doesn't work. It probably has something to do with JBoss module loading, but I don't know if this is a bug or normal behavior.
I found various similar problems on StackOverflow, but no applicable solution. One of the suggestions is to use ServletContext like so:
#Resource
private WebServiceContext wsContext;
...
ServletContext servletContext = (ServletContext)this.wsContext.getMessageContext()
.get(MessageContext.SERVLET_CONTEXT);
servletContext.getResource(resourcePath);
But, when I try to obtain MessageContext in this manner, I get an IllegalStateException. So I am basically stuck. Any ideas?
I ran into this same problem, and rather than define the resource as a shared module, I ended up working around this by using a ServletContextListener in my WAR.
In the contextInitialized method, I got the ServletContext from the ServletContextEvent and used its getResource("/WEB-INF/myResource") to get the URL to the resource inside my WAR file. It appears that in the ServletContextListener, the .getResource() method resolves as expected rather than to the "/modules/system/layers/base/org/jboss/as/ejb3/main/timers/" url. That URL can then be stored in the ServletContext for later use by your servlets or in an injected ApplicationScoped CDI bean.
#WebListener
public class ServletInitializer implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
final ServletContext context = sce.getServletContext();
final URL resourceUrl = context.getResource("/WEB-INF/myResource");
context.setAttribute("myResourceURL", resourceUrl);
} catch (final MalformedURLException e) {
throw new AssertionError("Resource not available in WAR file", e);
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
or
#WebListener
public class ServletInitializer implements ServletContextListener {
#Inject
private SomeApplicationScopedBean myBean;
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
final ServletContext context = sce.getServletContext();
final URL resourceUrl = context.getResource("/WEB-INF/myResource");
myBean.setResourceUrl(resourceUrl);
} catch (final MalformedURLException e) {
throw new AssertionError("Resource not available in WAR file", e);
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
We had a similar problem and our fault was that we tried to access the static resource through the raw path instead of using the input stream the resource is providing - the following code works for us even when deploying a non-exploded .war-file.
final URL resource = this.getClass().getResource(FILE);
try (final InputStream inputStream = resource.openStream();
final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
// Use bufferedReader to read the content
} catch (IOException e) {
// ...
}
I finally gave up and put my resource files in a new JBoss module, as described in this link.
https://community.jboss.org/wiki/HowToPutAnExternalFileInTheClasspath
It works, but the downside is that there are two deployment targets so things are more complicated. On the upside, the size of the WAR file is reduced, and I don't have to redeploy the application if only some of the resources have changed.
I was recently trying to figure out how to access a file within my own war in Java. The following is how the java classes and resources are packaged in the war file:
WAR
`-- WEB-INF
`-- classes (where all the java classes are)
`-- resourcefiles
`-- resourceFile1
My target file was resourceFile1. To get that file, I just did the following in code:
InputStream inStream = this.class.getClassLoader().getResourceAsStream("resourcefiles/resourceFile1");
In this case the resource files would need to be in the same folder as the classes folder containing the java classes. Hopefully others find this helpful.
This sample code works for wildfly deployed and tested on openshift.
I think it is a wildfly problem I downland wildfly and tried on local I also get the error.
Check sample project on github
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
#Controller
#RequestMapping
public class FileDownloadController {
private static final Logger logger = LoggerFactory.getLogger(FileDownloadController.class);
private static final String DOC_FILE = "file/ibrahim-karayel.docx";
private static final String PDF_FILE = "file/ibrahim-karayel.pdf";
#RequestMapping(value = "/download/{type}", method = RequestMethod.GET)
public void downloadFile(HttpServletRequest request, HttpServletResponse response,
#PathVariable("type") String type) throws IOException {
File file = null;
InputStream inputStream;
if (type.equalsIgnoreCase("doc")) {
inputStream = getClass().getClassLoader().getResourceAsStream(DOC_FILE);
file = new File(Thread.currentThread().getContextClassLoader().getResource(DOC_FILE).getFile());
} else if (type.equalsIgnoreCase("pdf")) {
inputStream = getClass().getClassLoader().getResourceAsStream(PDF_FILE);
file = new File(Thread.currentThread().getContextClassLoader().getResource(PDF_FILE).getFile());
} else{
throw new FileNotFoundException();
}
if (file == null && file.getName() == null) {
logger.error("File Not Found -> " + file);
throw new FileNotFoundException();
}
String mimeType = URLConnection.guessContentTypeFromName(file.getName());
if (mimeType == null) {
System.out.println("mimetype is not detectable, will take default");
mimeType = "application/octet-stream";
}
System.out.println("mimetype : " + mimeType);
response.setContentType(mimeType);
/* "Content-Disposition : inline" will show viewable types [like images/text/pdf/anything viewable by browser] right on browser
while others(zip e.g) will be directly downloaded [may provide save as popup, based on your browser setting.]*/
response.setHeader("Content-Disposition", String.format("inline; filename=\"" + file.getName() + "\""));
/* "Content-Disposition : attachment" will be directly download, may provide save as popup, based on your browser setting*/
//response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getName()));
response.setContentLength(inputStream.available());
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
}
}
Had the same issue with Wildfly and not-exploded WAR and using Spring and ServletContextResource I have got around it like this:
[org.springframework.core.io.]Resource resource = new ServletContextResource(servletContext, "WEB-INF/classes/resource.png");
In the same #Service class I also had:
#Inject
private ServletContext servletContext;
I decided so:
#Autowired
private final ApplicationContext ctx;
private final Path path = Paths.get("testfiles/load")
ctx.getRosource("classpath:" + path);
I read this solution, that lead us to use getResourceAsStream(...) instead of getResource() inside Wildfly. I just test it on Wildfly 19 with myApp.ear deployed from console.