I have an JavaEE application which needs some certain system properties configured during the runtime.
During the development phase, we set the properties in the .pom.xml to make it work:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<systemProperties>
<xxx>true</xxx>
</systemProperties>
</configuration>
</plugin>
However I wonder how about if we deploy the application in the production environment?
I have though of set the property during the initialization of the servlet, but it seems that I can not modify the system property once the jvm is runing(from this post):
The JVM memory parameters for a Hotspot Java implementation can only
be set via the command line options when the JVM is launched /
started. Setting them in the system properties either before or after
the JVM is launched will have no effect.
And I have tried to set the properties in the web.xml (here):
<env-entry>
<env-entry-name>xx</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>xx</env-entry-value>
</env-entry>
But it seems like it does not take affect.
Then I wonder how to solve it?
BTW, in the production environment we may run more than one application under a shared tomcat, so we prefer to set the properties under the application context only.
Environmental Entries
The <env-entry> won't make it for you because Environmental Entries are availble through JNDI and not through the System properties facade.
So if you configure some entry as below in your deployment descriptor file:
<env-entry>
<env-entry-name>myProperty</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>some-value</env-entry-value>
</env-entry>
Accessing the property in codebase as below will return a null reference:
System.getProperty("myProperty");
Though it should be retrieved with a context lookup as follows:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
// Look up for your property "myProperty"
String myPropertyValue = (String) envCtx.lookup("myProperty");
The main advantage is that you will be able to have a property which value will vary from one web application to another. But this way, and as the amount of your deployed artifacts will rise, it will be hard to maintain this and I think, based on my humble experience, it is not advised for production environments.
System Properties
If you are targeting a production environment, I advice using system properties (Your code source should then be updated to follow the style).
Since you are using Tomcat as an Application Server, you have one way that will let you have centric property definition, that is to declare a file named setenv.sh (.bat for Windows OS) where you export all the needed System Propeties, e.g of content it might have:
export CATALINA_OPTS="$CATALINA_OPTS -DmyProperty=some-value"
There is no more additional configuration needed, and this file will be processed by default when launching the JVM instance if present under $CATLINA_HOME/bin or $CATLINA_BASE/bin.
you are confusing web.xml env entry with system environment with java system properties
ask your ee server admin to add those in server definition such as
-Dpropname=true
Related
I have an app which is dockerized like this:
FROM openjdk:11-jre-slim as jdkbase
FROM jdkbase
COPY target/dependency-jars /run/dependency-jars
COPY target/resources /run/resources
ADD target/app-1.0.2.jar /run/app-1.0.2.jar
CMD java -jar run/app-1.0.2.jar
This app uses some configs from application.properties. I push this docker image into my private registry for using it inside Kubernetes cluster.
There is no problems unless i have to change any properties in application.properties. Lets say, my database URL was changed. So, I have to undate it in application.properties and then force my app to use updated configs. I've tried to edit application.properties inside the running docker container and then restart the container. As a result, after i restart my container it has edited application.properties BUT the app still uses old URL.
The only way i've found to force the app to use new configs is to commit changed container into new image and then start the new image.
It works, but it doesn't seems to me to be an optimal solution: like, after every changes in app's configs I have to recreate image, which is +300Mb of data, I have to push this new image into registry, I have to recreate Kubernetes pod from new image... It looks like too much unnecessary actions to just change one URL.
So, is there any other more optimal way to work with needs to change application.properties from time to time?
If this is a spring boot application is easy to define a variable as an environment variable. If an environment variable is present it overwrite the variable with the same name defined in your application.properties.
What happens is called externalized configuration and it is defined here :
Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments. You can use properties files, YAML files, environment variables and command-line arguments to externalize configuration. Property values can be injected directly into your beans using the #Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via #ConfigurationProperties.
Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:
Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
#TestPropertySource annotations on your tests.
#SpringBootTest#properties annotation attribute on your tests.
Command line arguments.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property)
ServletConfig init parameters.
ServletContext init parameters.
JNDI attributes from java:comp/env.
Java System properties (System.getProperties()).
OS environment variables.
A RandomValuePropertySource that only has properties in random.*.
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
Application properties outside of your packaged jar (application.properties and YAML variants).
Application properties packaged inside your jar (application.properties and YAML variants).
#PropertySource annotations on your #Configuration classes.
Default properties (specified using SpringApplication.setDefaultProperties).
So you can pass at runtime an environment variable to your docker to solve this problem. You don't need to recreate the Docker image each time.
We are running our application using Tomcat-7 in Windows environment. We are using Shibboleth IDP for our application, due to this we need to set system property at the container level to identify one new property called "idp.home". So we found that the property can be set in "catalina.properties". We set it and it was running successfully by using "idp.home" property but the problem is that if we use the same compiled war file in another machine, the property "idp.home" is not working.
cataline.properties
idp.home=../../IdP
Structure:
Build --> IdP
--> tomcat-->conf-->catalina.properties
Queries:
1) Is custom property "idp.home" cached in tomcat some where?
2) do we need to set "idp.home" property in any file along with the "catalina.properties" in tomcat.
3) Is there any other way to inform tomcat about "idp.home"?
Thanks in advance.
We can pass system properties with the -D parameter as VM arguments to tomcat, for example "-Dmy.prop.name=value"
or
Place system properties in a property file and specify the path of the property file in VM arguments, for example -Dcom.propertyfiles="path of the property File"
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.
We are developing an application that is deployed in a JBoss.
we would like to define a properties files like this:
URL_DEVELOPMENT.properties = ...
URL_TEST.properties = ...
URL_PRODUCTION.properties = ...
and define an environment variable in the JBoss which contains the information about the execution context
for example --> ENVIRONMENT = DEVELOPMENT
Anyone knows:
How to set environment variables in JBoss.
How to get these variables from an applicacion deployed in JBoss in runtime execution?
The easiest and most straight forward way is to log into jboss web admin:
www.yoururl:9990
Then under configuration, look for system property.
At runtime, it is very easy: System.getProperty(yourPropertyKey) and the good thing is that a change in any of these properties is reflected immediately at runtime.
The other scenario is to open up standalone.xml
<server ...>
<system-properties>
<property name="eclipselink.archive.factory" value="org.jipijapa.eclipselink.JBossArchiveFactoryImpl"/>
</system-properties>
</server>
The other option is to read on jboss cli and configure system properties from there. (Only useful if you want to work with remote jboss and you cannot ssh into the server, and you cannot access the web admin)
I am using resthub to bootstrap my spring-backbone webapp.
https://github.com/resthub/resthub.github.io/blob/master/docs/spring/layout.md#environment-specific-properties
According to https://github.com/resthub/resthub.github.io/blob/master/docs/spring/mongo.md . They already have a default database properties file.
Things work fine so far, but I have to deploy the project on heroku, which has different dbname, port & other settings.
To override default db properties provided by resthub, a file named database.properties can be created in the classpath. But I want that file to be picked only for deployment on heroku.
How shall I proceed about configuring it such that it works locally on intellij with default db properties but takes up database.properties for heroku.
I have already read about Maven Profiles, but it talks about how to have different files for different environments and I'm looking for not to have a particular file for a particular environment, instead use the default one provided in one of the resthub jars.
Update
Just went through
What is the order of precedence when there are multiple Spring's environment profiles as set by spring.profiles.active
resthub-mongodb profile is always olded, in case of production (identified by -Dprod=true), a new profile called heroku is loaded which in turns loads prod\database.properties
<context:property-placeholder location="classpath*:prod/database.properties"/>
But I guess the mongo related beans are already defined using properties defined in resthub-mongodb & hence, loading loading heroku profile to change the db properties doesn't work.
What is the solution in such a case ?
Possible Solution
For now, I ended up using one of the profile - heroku or resthub-mongodb depending on the system property.
Is there any other way ?
You can let maven handling your environements with resource configuration.
Add in your pom or parent pom :
<env>local</env>
...
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>${basedir}/src/main/config/${env}</directory>
<filtering>false</filtering>
</resource>
</resources>
This allows you to provide multiple configuration directories ; one for each your environment : local, heroku ...
With this conf, you will be able to provide a specific database.properties file for your heroku environment while keeping resthub defaults locally (not providing custom database.properties in config/local):
src/
main/
config/
local/
heroku/
database.properties
Adding -Denv=heroku option to any of your maven command run will add all config/ directory content in your classpath. local is the default environement and used if no option is given.