Java - How to access a file in the WEB-INF folder WITHOUT servlet context
Asked Answered
S

3

6

I have a central class (RuntimeConfiguration) that holds around 100 parameters for my application. These parameters are taken from an XML file which I stored in the WEB-INF folder. On my development box, I hard-coded the path, but for very obvious reasons, this is not a productive solution.

For portability reasons (my application might be executed on a Tomcat or a Jetty and under Windows OR Unix), I want to have a generic access way to this file from within my RuntimeConfiguration class.

I know, I can use getRealPath in case I have a servlet, BUT RuntimeConfiguration is NOT started within a servlet context. It is invoked from a job (a crawler) which in turn is started from quartz job control via applicationContext.xml. Now this seems to be the core of my problem.

Can anyone tell me, how to get the absolute path to my XML in this environment?

I tried this ClassLoader approach, but it gives me a NullPointerException:

ClassLoader loader = this.getClass().getClassLoader();
loader.getResource("WEB-INF");
result = loader.getSystemResource(RTCF).toString(); 

I also tried

URL url = this.getClass().getClassLoader().getResource("WEB-INF");

which also throws a null pointer exception

Can anyone give me a portable solution, one that works on my dev-machine and on a production system where my application is deployed as a WAR-file?

By the way, the method should at best be able to handle Windows AND Mac OS X / Unix based systems.

Sestet answered 14/10, 2014 at 8:53 Comment(3)
Have u debug the solutions u have mentioned in question ? Do you get ClassLoader object ? At which point you get NPE ? You can add stacktrace for better understanding.Troublemaker
Is RuntimeConfiguration is declared as bean in your web application context ?Troublemaker
no. RuntimeConfiguration is not registered as a bean in my web application contextSestet
A
1

You can always use the holder pattern : you create a class, with a static field (with its static getter) that will contain the real path of the WEB-INF directory. This class should implement ApplicationContextAware

You create in root spring application context an instance of this holder class, Spring give it a pointer to the ApplicationContext that will be a WebApplicationContext as you are in a web application.

In the init method, you use the WebApplicationContext to get the path and store it in the static field. Thanks to Spring, you are sure that the method is called once and only once before the application really starts.

Now anywhere in the application where you want to get the path, you can use it by calling the static getter.

public class ResourcePathHolder implements ApplicationContextAware, InitializingBean{
    private WebApplicationContext wac;
    private static String resourcePath;
    private static ServletContext sc;
    private String path = "WEB-INF";

    public static String getResourcePath() {
        return resourcePath;
    }

    public static ServletContext getServletContext() {
        return sc;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        wac = (WebApplicationContext) applicationContext;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ResourcePathHolder.sc = wac.getServletContext();
        ResourcePathHolder.resourcePath = sc.getRealPath(path);
    }
}

This implementation also gives you access to the ServletContext to allow you to use ServletContext.getResource()

EDIT :

Using the full path may not work on production servers that would not explode the war. From the javadoc for ServletContext.getRealPath() : This method returns null if the servlet container is unable to translate the given virtual path to a real path. But in that case, you can always access to the files as resources with ServletContext.getResource() or ServletContext.getResourceAsStream()

Alrich answered 14/10, 2014 at 10:1 Comment(6)
bugger! I was to quick making this the working answer. It works on my development machine, but on the server there is a part of the path omitted - see output here: /opt/apache-tomcat-8.0.12/null/SNC_Runtime_Configuration.xml Would you know, why this might happen? Here is my snippet from applicationContext.xml (Maybe I' m missing something there): <bean id="contextWebApplicationContextProvider" class="de.comlineag.snc.appstate.ResourcePathHolder"></bean>Sestet
@siliconchris: that's the reason why I included a direct access to the ServletcContext : see my editAlrich
OMG - I must be somewhat cursed with absolut unapt ability, I really don't get this. If I use getResource, I get a this path back: jndi:/localhost/SocialNetworkConnector/WEB-INF. But how would I access any file with that? All I'd like to have is a simple full qualified path to the configuration file. Isn't there some easy method to do this? I mean, the server knows it's location where it is running, so either it should provide some path, or some global variable or whatever, to prepend to a file path, so that I can access a file below a well known directory. Am I thinking completely wrong here?Sestet
@Sestet : Say your config file is /WEB-INF/conf.xml, can you take InputStream confStream = servletContext.getResourceAsStream("/WEB-INF/conf.xml"); and then feed confStream into your XML parser ?Alrich
Hm, I would need to rewrite my RuntimeConfiguration class for that. Currently it has methods like these: private void setRuntimeConfiguration(String configFile){ logger.debug(">>> setting runtime configuration"); String debugMsg = ""; try { File file = new File(configFile); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(file); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath();Sestet
@Sestet I never did it and have currently no access to a computer, but I've just thought to another way - if it works... ServletContext.getResource() gives an URL, an URL can be converted to an URI (URL.toURI()) and File constructor accepts an URI. Pretty sure it will work on your dev system, but on prod one ...Alrich
T
0

In spring web application, you cannot get context path until you declare some class as bean in your web application context. Add below code in your RuntimeConfiguration class

@Autowired
private ServletContext servletContext;

public {desired returnType} getDirectoryPath(){
    File webInfPath = new File(this.getClass().getClassLoader().getResource("WEB-INF"));
    // OR use getResourceAsStream() method
    InputStream is = RuntimeConfiguration.class.getClassLoader().getResourceAsStream("WEB-INF");
}

OR

If you don't want to do autowiring, then you can implement RuntimeConfiguration with ServletContextAware interface. Just like below, to get servlet context object in this class :-

public class RuntimeConfiguration implements ServletContextAware {

    private ServletContext context;
    private ServletConfig config;

    @Override
    public void setServletContext(final ServletContext servletContext) {
        this.context = servletContext;
    }

    public ServletContext getServletContext()[
        return context;
    }
    //do other tasks
}

OR

Have a look at ServletContextResource ServletContextResource - Spring Docs

Troublemaker answered 14/10, 2014 at 9:47 Comment(0)
P
0

Just put it in your classpath and load it with "getResourcesAsStream()", or better make it a ".properties"-file and load it the usual way:

ResourceBundle config = ResourceBundle.getBundle("filename");
Proletariat answered 14/10, 2014 at 10:2 Comment(1)
sorry, but the configuration file has over 200 options, some with need for same name in different sections. So this would create a whole lot of new issues for me.Sestet

© 2022 - 2024 — McMap. All rights reserved.