mazatwork
mazatwork

Reputation: 1305

PropertyPlaceholderConfigurer: Use external properties file

How to configure PropertyPlaceholderConfigurer to use properties files relative (some directories up) to the war?

We have running a war multiple times and each war should read its configuration for example from ../../etc/db.properties.

Update:

Yes, the properties files are outside the war. The directory structure is:

/htdocs/shop/live/apache-tomat/webapps/shop.war should read /htdocs/shop/live/etc/db.properties

and

/htdocs/shop/test/apache-tomat/webapps/shop.war should read /htdocs/shop/test/etc/db.properties

Upvotes: 3

Views: 9340

Answers (4)

YumeNoShizuku
YumeNoShizuku

Reputation: 21

Somehow I wasn't able to get the desired path following others' methods, so here is my working version, based primarily on Dmitry's answer (usage in xml is identical), while isReadable() and getInputStream() looks more like mazatwork's version:

public class RelativeResource extends AbstractResource {
    private final String relativePath;

    public RelativeResource(String relativePath) {
        this.relativePath = relativePath;
    }

    @Override
    public String getDescription() {
        return "RelativeResource [" + relativePath + "]";
    }

    @Override
    public boolean isReadable() {
        return true;
    }

    @Override
    public boolean isOpen() {
        return true;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        String rootPath = this.getClass().getResource("/").getPath();
        rootPath = URLDecoder.decode(rootPath, "UTF-8");
        if (!rootPath.endsWith(File.separator)) rootPath += File.separator;
        String path = rootPath + relativePath;
        return new FileInputStream(path);
    }
}

Upvotes: 0

Dmitry Spikhalsky
Dmitry Spikhalsky

Reputation: 5820

My code, based on mazatwork solution:

public class RelativeResource extends AbstractResource {
    private final String relativePath;

    public RelativeResource(String relativePath) {
        this.relativePath = relativePath;
    }

    @Override
    public String getDescription() {
        return "RelativeResource [" + relativePath + "]";
    }

    @Override
    public boolean isReadable() {
        File resourceFile = new File(getAbsoluteFileLocation());
        return resourceFile.exists();
    }

    @Override
    public boolean isOpen() {
        return true;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new FileInputStream(getAbsoluteFileLocation());
    }

    private String getAbsoluteFileLocation() {
        return Paths.get("").toAbsolutePath().resolve(relativePath).toString();
    }
}

After that we can write in xml for example:

<bean id="configurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:application.properties</value>
            <value type="com.blabla.RelativeResource">overrideProperties.properties</value>
        </list>
    </property>
    <property name="ignoreResourceNotFound" value="true"/>
</bean>

Pros of this method - you don't hack Spring Context and don't stick to this hacked context implementation, you can use any (for example, not XmlWebApplicationContext, but ClassPathXmlApplicationContext) from Spring distribution.

Upvotes: 1

mazatwork
mazatwork

Reputation: 1305

Finally, we have introduced a new resource type "relative:":

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true" />
    <property name="locations">
        <list>
            <value>classpath:db.properties</value>
            <value>relative:../../../etc/db.properties</value>
        </list>
    </property>
</bean>

We have extended XmlWebApplicationContext to inject custom resource handling:

public class Context extends XmlWebApplicationContext {
    @Override
    public Resource getResource(String location) {
        if (location.startsWith(RelativeResource.RELATIVE_URL_PREFIX)) {
            String relativePath = location.substring(RelativeResource.RELATIVE_URL_PREFIX.length());
            return new RelativeResource(getServletContext(), relativePath);
        }
        return super.getResource(location);
    }
}

Here is the relative resource class:

public class RelativeResource extends AbstractResource {
    public static final String RELATIVE_URL_PREFIX = "relative:";

    private final ServletContext servletContext;
    private final String relativePath;

    public RelativeResource(ServletContext servletContext, String relativePath) {
        this.servletContext = servletContext;
        this.relativePath = relativePath;
    }

    @Override
    public String getDescription() {
        return "RelativeResource [" + relativePath + "]";
    }

    @Override
    public boolean isReadable() {
        return true;
    }

    @Override
    public boolean isOpen() {
        return true;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        String rootPath = WebUtils.getRealPath(servletContext, "/");
        if (!rootPath.endsWith(File.separator)) rootPath += File.separator;
        String path = rootPath + relativePath;
        return new FileInputStream(path);
    }

}

Upvotes: 3

Kevin Bowersox
Kevin Bowersox

Reputation: 94429

In your configuration you can specify the properties from the classpath instead of relative to the configuration file.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

For this to work you must make sure that the properties file makes it to the classpath.

Upvotes: 0

Related Questions