I use Neatbeans to create a Java Class Library Project. This class (name: TestDB.java) is to do some DataBase jobs. I wrote a "config.properties" file to config some properties. The config.properties is located in the Project folder. In my class TestDB.java:
Properties properties = new Properties(System.getProperties());
String configFile = "config.properties";
properties.load(new FileInputStream(configFile));
String param1 = properties.getProperty("Postgre.Driver1");
I can use a test case to get the property, That's OK.
When I build this project and create a jar file. I want another web application to use this jar file to do some DB jobs. I already include the jar in my webapp and bulid a webproj.war. When I place this webproj.war to Tomcat and run the webapp. I can't get the property because I don't know which folder in the Tomcat webapp to place the config.properties. Anyone can help me to solve this question ?
Sorry for the long answer, but I'm trying to cover the background as I think your question is a little bit confused.
This answer tries to clarify your understanding of web-apps and reusability a little more generally.
First, it's important to understand that applications typically have to run in multiple environments - dev, sit, uat, production, etc. If you include environment specifics in your binary (jar or war) that means you can only use that binary in a single environment.
The original J2EE assumption was that these environment specifics would be managed by the container (Tomcat in this case). This is what the answer referenced in the first comment of your question is doing. Basically the container provides resources registered in JNDI, which your application can look up.
Now, I think it's fair to say, the J2EE folks where wide of the mark with some of the features, the JNDI resource mechanism being one instance of this, which isn't commonly used these days (at least not in the applications I'm used to).
Now, to try and answer your question.
The first important thing to recognise is that the reason for putting code into a seperate jar is to make it reusable. Since Spring burst onto the scene, one of the fundimental ways to achieve reusability is with dependency injection. Spring aside, dependency injection is nothing more complicated that passing the resources a particular object needs at the time you create that object.
For example, your TestDB class might be instantiated as follows:
TestDB testDB = new TestDB(dataSource);
This still doesn't solve the problem of instantiating the DataSource, however it does place this responsiblity with the code using TestDB rather than TestDB itself.
The simple way to instantiate the DataSource would be include a properties file in the war's classpath (in WEB-INF/classes), which would allow you to do the following (note the DataSource instantiation will be database specific):
Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("config.properties"))
DataSource dataSource = new PGSimpleDataSource();
dataSource.setServerName(properties.get("database.server.name"));
...
So this gets you to the point where you're loading a properties file from within your war. However as I stated, this makes the war environment specific.
The best way I know to solve this is to use an exploded war with a symlink to the environment configuration. To do this, you use a context file which you would place in [Tomcat root]/conf/Catalina/localhost.
For example, the given context file:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/my-app" docBase="/local/apps/my-app/war" distributable="false" allowLinking="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
You would have a directory structure like this:
local
+ apps
+ my-app
+ war
+ WEB-INF
+ classes
+ env -> /local/apps/my-app/env
+ env
Where /local/apps/my-app/war/WEB-INF/classes/env is a symlink to /local/apps/my-app/env.
Your properties would now be loaded like this:
Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("env/config.properties"))
This gives you two releaseable components - the application and the configuration that are simply unpacked to install them.
Related
We have a database library that gets the connection information (user, host, etc) from a properties file. This file can be a config.properties file located in the classpath, or next to the execution jar or can be passed as an argument -Dproperties=/path/to/myConfig.properties.
We also have several applications that use this library, so each one has its own config.properties file used in its own execution.
But now I'm creating two web applications that use the same library. So, if I deploy them in Tomcat (war file), I have two options (to my knowledge):
1.- Include each config inside the WAR file. But with this, every time I need to tweak something in the config.properties I'll have to repack the war.
2.- Pass the -Dproperties parameter as an execution argument of Tomcat. But different war deployments will have to share the same properties file.
Is there a way around this?
Can I pass the -D argument to a specific deployment in Tomcat (or any other server)?
PS: This is one of the scenarios we have, but is not constraint to database connection info. We have other libraries that get parameters through config.properties file.
EDIT: I want to be able to have different config.properties file for each deployment. Not the same properties shared among them.
I think I found a way around using self contained webserver inside the application, like Jetty.
We've a similar requirement in which we share a common property file between different applications deployed into JBoss EAP server.
In $JBOSS_HOME/bin/standalone.conf file you can add configuration file path as below:
JAVA_OPTS="$JAVA_OPTS -DCONFIG_LOCATION=/external/config/configuration.properties"
Start the server with above specified property and within your application you can read this property file with apache commons-configuration api as below:
try {
props = new PropertiesConfiguration(System.getProperty("CONFIG_LOCATION"));
FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();
// Delay 30s
// strategy.setRefreshDelay(30000);
props.setReloadingStrategy(strategy);
} catch (ConfigurationException e) {
e.printStackTrace();
}
With this reload strategy you can change your properties while your server is running. Also you can specify the interval after which all properties specified in the external file needs to be refreshed within your application without bouncing it. Hope this helps!
You can create an environment variable whose value will be the path where the properties file are located. Later use this environment variable will creating bean for property placeholder config.
for UNIX, you can add in your bash profile file
export CONF_DIR=/path/to/conf
And in spring context file, add this
<context:property-placeholder
location="file:///${CONF_DIR}/path/myConfig1.properties,
file:///${CONF_DIR}/path/myConfig2.properties"
properties-ref="applicationDefaultProperties" ignore-resource-not-found="false"
ignore-unresolvable="false"/>
So, when you want to change any thing in the properties file, you can change at one location, and then restart the application to load the new values in your app.
So, if your config file is this
db.user=username
db.password=password
Inside java class, you can use the keys as like this
#Value("${db.user")
private String username;
#Value("${db.password")
private String password;
The solution I found for my problem is using an embedded web server in my application. In my case, I'm using Jetty.
Now I pack my application as an executable jar and pass the system parameters as -D arguments and they live inside the instance of the application.
Like this:
java -Dproperties=config.properties -jar java_app_with_embedded_server.jar
In my Spring Boot app i need to call to:
System.setProperty("java.security.auth.login.config", authConf);
where authConf seems to be expected as an absolute path to the file.
the problem is, my Spring Boot app is packaged and executed as a jar and i want to package the file inside the jar.
The answer provided in this question might work only when a WAR is deployed in a server. It doesn't seem to work when we run JARs with embedded container.
is there way i could set a relative path to java.security.auth.login.config to refer to my conf file packaged within my jar ?
I know that the answer is about 6 years too late, but I recently faced the same issue and found and answer. Maybe my answer will at least help the next dev who will face this issue. I was able to solve it is by first creating a javax.security.auth.login.Configuration of type JavaLoginConfig and then use the Configuration.setConfiguration(configuration method) of the same Configuration class. Using this method I was able to bundle our jaas.conf inside the jar and use a relative path to it.
To give a concrete example:
Get the URL of the security configuration file you want to use
URL configLocation=YourClass.getClassLoader().getResource("resources/jaas.conf);"
Create the JavaLoginConfig configuration
Configuration secConfig=Configuration.getInstance("JavaLoginConf", new URIParameter(configLocation.toURI());
Set the configuration to the one you created in step 2
Configuration.setConfiguration(secConfig);
I need to read an application.properties file from a servlet application using Tomcat container. The file can not be included in my war so it can't be under webapps or Tomcat root folder in any ways. The file has to be somewhere in the folder. I also can not use FileInputStream to read the properties file. Only option I have is to define a JNDI name for a folder / directory and look that JNDI properties during runtime to find the folder location to read the file. Is thee any working example out there?
I have chalked out a solution for myself reading the following similar posts and articles.
The Context Container
Reading a global variable from tomcat with JNDI. Example not working
java:comp/env is not bound
I have defined a Environment inside my Context for my web application under
\conf\Catalina\localhost\mywebapp.xml as follows....
<Environment name="propertiesfilelocation" value="E:\\tmp\\application.properties"
type="java.lang.String" override="false"/>
Then accessed my properties file using a JNDI lookup to get the file name.
Context ctx = new InitialContext();
Context envCtx = (Context)ctx.lookup("java:comp/env");
String propertiesFileLocation = (String) envCtx.lookup("propertiesfilelocation");
LOGGER.info("String property === " + propertiesFileLocation);
properties.load(new FileInputStream(propertiesFileLocation));
#home : Yes you are right it involved FileInputStream. However, I am happy with the solution because I am no longer hard coding my folder location inside my Java code which makes my app more portable.
I have a slightly different approach to a similar problem. I also toyed with the idea of JNDI but I found this a bit of overkill.
I found another article by BalusC. It allows you to specify folder relative to your tomcat installation and deploy all your properties files there. So you keep configuration external to your application but you don't require seperate configuration for every properties file for every app.
Here it the link:
Where to place and how to read configuration resource files in servlet based application?
and the preferred choice is as follows:
Put it in the classpath, so that you can load it by
ClassLoader#getResourceAsStream() with a classpath-relative path:
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("filename.properties"));
Here filename.properties is supposed to be placed in one of the roots
which are covered by the default classpath of a webapp, e.g.
webapp/WEB-INF/lib, webapp/WEB-INF/classes, appserver/lib or JRE/lib.
If the properties file is webapp-specific, best is to place it in
WEB-INF/classes. If you're developing a project in an IDE, you can
also drop it in src folder (the project's source folder).
You can alternatively also put it somewhere outside the default
classpath and add its path to the classpath of the appserver. In for
example Tomcat you can configure it as shared.loader property of
tomcat/conf/catalina.properties.
I hope this helps.
Allan
I have two spring projects both using maven. The first is a client for some api and the second is a console program that, in part, utilises that client.
I have packaged up the client into a jar and referenced it in the pom for the console program.
I have managed to get this working, just about, but I am not very happy with the solution:
1) The first problem I ran into was that each of the context xml files were named "applicationContext.xml". Therefore, I couldn't work out any way to reference the context file in the client, without renaming it to something else e.g. clientContext.xml. This works but is there any other way to reference it explicitly?
2) The next issue was how to invoke the clientContext.xml from within the console program. To do this, I have added <import resource="osrdClientContext.xml"/> to the applicationContext.xml of the console program and this seems to allow it to correctly find all of the defined beans. I'm not sure if this is best practice though?
3) Within clientContext.xml, I need to reference a properties file and so have the line <context:property-placeholder location="classpath:api.properties" />. This works when running the client on its own but appears to get ignored (or fails to find the file) when running the console program. The api.properties file is in the root of the packaged jar for the client and the jar is in the classpath of the console program. The only workaround I have found is to manually copy the properties file into the console program, at which point it is found without any problem.
4) Both projects have a resources directory with sub-directories "dev", "beta" and "prod". This allows me to define different properties depending on the maven profile I want to run against. This works fine for the individual projects but when I package the client, it only includes the properties files for the profile I am running against (which makes sense). However, that then means if I run the console project against profile "beta", it will still run the client against whatever profile it was packaged against. It would be handy to be able to package all of the properties files and get the client to run in the same profile as whatever is depending on it. Is that possible/a good idea?
Ad 1: The common place to put your JAR-based XML contexts is inside META-INF/your/project/name folder. You can check for example spring-batch-admin project. Also nowdays it is more common to name the context files {name}-context.xml (e.g. central-context.xml).
By following the advice above, you should not have problems with name conflicts. However it should be possible to overcome such problem by using classpath* pseudo protocol in your import definition:
<import resource="classpath*:do/not/put/in/root/this-can-be-duplicate.xml"/>
Ad 2: This is completely legit. You can see the same practice in the Spring Batch Admin example as linked above. Just add the classpath: or classpath*: to the resource path.
Ad 3: That is very strange and I have no clue to what is going on there.
Ad 4: This is possible to achieve via Spring profiles (not Maven profiles):
<beans profile="dev">
<context:property-placeholder location="classpath:META-INF/dev/my.properties"/>
</beans>
or via new SpEL support:
<context:property-placeholder location="classpath:META-INF/#{systemProperties['my.jvm.property']}/my.properties"/>
However what I like is to have a default properties and then let the main application be able to override them. This means, that your configuration will be on a single place and not inside the JAR. You can achieve this via properties hierarchy:
<context:property-placeholder location="classpath:META-INF/my-default.properties,classpath*:META-INF/my-optional-overrides.properties"/>
UPDATE just discovered that <context:property-placeholder> has problems with SPeL. However you can still use SPeL (and even other property configurers) when defining the PropertyPlaceholderConfigurer manually (i.e. via <bean>).
I need to have hibernate database config set from outside text file, how can I do it? Is there some kind of method for this, or do I have to make my own?
You can programaticaly configure hibernate within your app: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-programmatic
HTH
Hibernate configuration file (*.cfg.xml) have to be in the class path of your application's war.
But, you can maintain file like 'install.properties' in deployment folder of your application.
Inside it you can maintain database related properties.For eg.
database.name =
database.hostname=
database.username=
databse.password=
Use some predefined constant string inside 'hibernate.cfg.xml' that you can replace during time of application deployment task. For eg.
<property name="hibernate.connection.url">jdbc:mysql://$database.hostname/$database.name</property>
you can write an 'ant' task which 'unwar' the war then replace constant strings with values as mentioned inside 'install.properties' and then make 'war' again.
In this way you can separate out configuration settings from application code structure.
Hibernate configuration files have to be on the class path but can be outside your war. The exact way to add a directory or files to the class path will depend on your app server so you'll have to mention which one you're using (for Tomcat, see this previous question for example).