WAS Liberty profile - Read property using ClassLoader.getSystemResourceAsStream - java

We are migrating our application from WAS 6.1 to Liberty. Our application uses third party jars that read property files byInputStream is = ClassLoader.getSystemResource("myproperty.properties").
In WAS 6.1, we set server classpath to the location of myproperty.properties. We tried the below approaches to set classpath in Liberty, but nothing works
Approach 1: Set the below in jvm.options (D:\ConfigFiles\DEV\ - path containing myproperty.properties)
-Djava.class.path=D:\\ConfigFiles\\DEV\\
Approach 2: Setting the classloader in server.xml,
<library id="config">
<folder dir="${server.config.dir}/config/" includes="*.properties" scanInterval="5s"/>
</library>
<enterpriseApplication id="applicationEAR" location="application.ear" name="application">
<classloader privateLibraryRef="config"/>
</enterpriseApplication>
Please let us know if there is any other ways to override/set classpath in Liberty profile?

Try setting this property in jvm.options (instead of -Djava.class.path=path/to/propertyDir):
-Xbootclasspath/a:path/to/propertyDir
This will append the path of the property directory (containing your resource file) to the JVM's bootstrap classpath. Because this is an append, it should also work in Java 9+ (some related options have been removed in Java 9).
I suspect that the reason -Djava.class.path=... doesn't work is that the JVM gets the classpath from the WLP server script - so the system property is essentially applied too late in the startup of the server JVM.
You might also be able to put the properties file(s) in your JVM's lib/ext directory, but I haven't tested that. The -Xbootclasspath/a:path approach works for me on Mac - I assume it will also work on Windows.
HTH, Andy

If your end goal is to load a properties file, a more straightforward way to do this would be using a bootstrap/env/system property or <jndiEntry> in server.xml to store the location of the properties file, and then load it. For example, using an environment variable:
(in server.xml file)
<server>
<featureManager>
<feature>jndi-1.0</feature>
...
</featureManager>
<jndiEntry jndiName="configDir" value="D:\\ConfigFiles\\Dev"/>
</server>
Then, you can load the resource in your application like this:
#Resource(lookup = "configDir")
String configDir;
InputStream is = new FileInputStream(configDir + "/myproperty.properties");
Or, if you will always put your config property files somewhere under ${server.config.dir}, then you can utilize the built-in SERVER_CONFIG_DIR environment variable in Liberty:
String configDir = System.getenv("SERVER_CONFIG_DIR"); // equivalent to ${server.config.dir} in server.xml
InputStream is = new FileInputStream(configDir + "/myproperty.properties");
On the topic of managing configuration, check out MicroProfile Config (e.g. <feature>microProfile-1.2</feature>) which you may find useful: Configuring microservices with Liberty

Related

Liberty Server - ClassLoader.getSystemResource returns null

Developed a rest service which uses third party jars. Reviewing their code I found that they are using ClassLoader.getSystemResource("log4j.properties") to retrieve the data. This code returns null and henceforth I get null pointer exception always. The same code if I deploy in websphere 8.5.5.7 and set server classpath it is working fine.
Since I am using Liberty in my local machine this is not retrieving the path.
I also tried setting classloader in server.xml but no use.
<library id="config">
<folder dir="/properties/dev/" includes="*.properties" scanInterval="5s"/>
</library>
<enterpriseApplication id="AbcEAR"location="AbcEAR.ear" name="AbcEAR">
<classloader privateLibraryRef="config"/>
</enterpriseApplication>
Using ClassLoader.getSystemResource() will use the system classloader, which will only be able to see things from the JDK and whatever is on the classpath. Since Liberty does not use a traditional classpath and instead uses OSGi, the system loader will be of little use in Liberty from an application perspective.
Instead, use ClassLoader.getResource() from the thread context classloader, which will take into account the Liberty classloaders.

Need in understanding classpath in resource location and variable value set

Although I have been working in java for a while, there are many small things I have been ignoring, which at times have become bottleneck in productivity. I have difficulty in understanding this:
This is one of the bean.xml which gets placed in the final .war file (in a web application, built with spring framework).
<context:property-placeholder
location="classpath:/${deploy.env}/com.example.config/db.properties"
ignore-resource-not-found="false" />
I have following doubts:
1) At the time of building the code, i did like this for passing value of deploy.env
mvn clean install -Ddeploy.env=local
I ran the mvn in debug mode and could see this set to local. Now, the thing is, in the .war that gets generated, it is still ${deploy.env} (see above snippet). Doesn't this get replaced in the final .war? If not, then how do we pass the value which we intend to set?
2) what does "classpath:/${deploy.env}/com.example.config/db.properties" mean? Who sets the value of classpath? Are classpath capable of providing the location of resource files as well?
Assuming deploy.set --> local, so would this get translated to:
classpath:"/local/com.example.config/db.properties"
So does this mean db.properties would be present at: /local/com.example.config/db.properties
Any inputs to understand this would be of great help.
deploy.env is either environment variable or system property available to the JVM at run time.
The classpath:/${deploy.env}/com.example.config/db.properties will be resolved at run when your war is running in the container.
Set deploy.env=whatever in the shell from where you starting the tomcat or set in the environment of the user which starts the tomcat.
mvn clean install -Ddeploy.env=local here the deploy.env system property is available at build time. This will not replace the value of your spring config.
classpath is where all your classes and libraries bundled in the war are available along with the tomcat libraries. The spring property configurer will look for the db.properties file in the classpath at location e.g. /local/com.example.config
Spring documentation to learn more
Some explanation on my blog post
As stated in the Oracle Web site: The CLASSPATH variable is one way to tell applications, including the JDK tools, where to look for user classes.
That classpath: is referring to that location in particular, whatever it is, so it will start looking for those resources defined by Spring from that location and on, until it finds the first match.
Also, if you have that as a property in Maven, the value can be replaced with the right plug-in and configuration; not quite useful when you want a build that can be used with many values within those .properties files for different environments.
You can use other prefixes as file:, http:, etcetera. But you are just wondering about classpath:.

Dynamic Change of files directory for jdbc.properties and log4j.properties

I'm working in a Java Web Project.
I need to change the folder of the files "jdbc.properties" and "log4j.properties" depending of the environment, because testing, demo and release have diferent values for those files.
I have this folders and subfolders:
c:\myProject\conf\dev
c:\myProject\conf\test
c:\myProject\conf\demo
I need to put diferent jdbc.properties and log4j.properties files in each of those folders
c:\myProject\conf\dev\log4j.properties
c:\myProject\conf\dev\jdbc.properties
c:\myProject\conf\test\log4j.properties
c:\myProject\conf\test\jdbc.properties
c:\myProject\conf\demo\log4j.properties
c:\myProject\conf\demo\jdbc.properties
The three project are in the same Server and in the same Apache (It is a Web Project)
First i made some changes to use a windows system variable to get the parent folder (c:\myProject). To do that, i made this on Spring appContext file:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>file:${PARENT_FOLDER}/conf/dev/jdbc.properties</value>
</property>
</bean>
"PARENT_FOLDER" is defined on Windows environment variables/system variable
Those changes works OK.
But, as you can see, I always loking for file on "/conf/dev"
I need to make dynamic the "dev" part of the path.
I Can't use Windows environment variables/system variable because the 3 environments are deployed on the same Server.
I'm trying to use a "property" (using ) on web.xml, but I don't know how to find the property in my Spring appContext file.
I definy the property like this:
<env-entry>
<env-entry-name>ENVIRONMENT</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Dev</env-entry-value>
</env-entry>
But I don't know how to access "ENVIRONMENT" property on Spring
I don't know what to do. I a little desperate
Can someone help me?
Thanks and sorry for my poor english
Have you considered using JNDI?
With JNDI you will define the db connection properties inside tomcat itself. This way your spring configuration is independent of the environment and you can deploy the same war on all environments. See also this.
If you need to run it locally that you can always use the 'new' spring environment profiles feature.
Other option (if JNDI is not an option and assuming you use maven) is the maven replacer plugin where you will generate the db.properties at build time.

How to deploy the same webapp with different logging? (Tomcat, Solr)

We are using multiple solr instances on tomcat but want that they log into different log files. How could we do this?
We are using the follwing xml file under tomcat/conf/Catalina/localhost to make it working:
<Context docBase="/pathtosolr/dist/apache-solr-1.4.0.war" debug="0" crossContext="true" >
<Environment name="solr/home" type="java.lang.String" value="/pathtosolr/solr" override="true" />
</Context>
Update: From Jems answer I found this documentation:
Tomcat offers a choice between settings for all applications or settings specifically for the Solr application.
To change logging settings for Solr only, edit tomcat/webapps/solr/WEB-INF/classes/logging.properties. You will need to create the classes directory and the logging.properties file. You can set levels from FINEST to SEVERE for a class or an entire package. Here are a couple of examples:
org.apache.commons.digester.Digester.level = FINEST
org.apache.solr.level = WARNING
Alternately, if you wish to change Tomcat’s JDK Logging API settings for every application in this instance of Tomcat, edit tomcat/conf/logging.properties.
See the documentation for the SLF4J Logging API for more information:
http://slf4j.org/docs.html
You might wanna take a look at this page: http://globalgateway.wordpress.com/2010/01/06/configuring-solr-1-4-logging-with-log4j-in-tomcat/
There seems to be no good solution:
2008 and 2010
except repacking the solr.war file with a different logging configuration file.
Update: see accepted answer!

Can I set the classloader policy for WebSphere in the ibm-web-bnd.xmi file?

I have a JEE application that runs on WAS 6. It needs to have the class loader order setting to "Classes loaded with application class loader first", and the WAR class loader policy option set to "Single class loader for application".
Is it possible to specify these options inside the EAR file, whether in the ibm-web-bnd.xmi file or some other file, so the admin doesn't need to change these setting manually?
Since the app is deployed via an automated script, and the guy who is in charge of deployment is off site, and also for some other political reasons, this would greatly help!
Thanks to #Matthew Murdoch's answer, I was able to come up with a solution. Here it is, in case it helps someone else.
I created a deployment.xml like this:
<?xml version="1.0" encoding="UTF-8"?>
<appdeployment:Deployment xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:appdeployment="http://www.ibm.com/websphere/appserver/schemas/5.0/appdeployment.xmi" xmi:id="Deployment_1241112964096">
<deployedObject xmi:type="appdeployment:ApplicationDeployment" xmi:id="ApplicationDeployment_1241112964096" startingWeight="1" warClassLoaderPolicy="SINGLE">
<classloader xmi:id="Classloader_1241112964096" mode="PARENT_LAST"/>
<modules xmi:type="appdeployment:WebModuleDeployment" xmi:id="WebModuleDeployment_1241112964096" startingWeight="10000" uri="AGS.war">
<classloader xmi:id="Classloader_1241112964097"/>
</modules>
</deployedObject>
</appdeployment:Deployment>
Make sure to change the name of your WAR file(s) to match (mine is called AGS.war).
I also changed the numbers in the xmi:id attributes, to be sure they are unique, though I'm not sure it it really matters that they be unique across applications.
Then, I put the deployment.xml file in the root of my EAR file, via ANT:
<ear destfile="${artifactsDir}/${earName}.ear" appxml="${projectName}_EAR/application.xml">
<fileset dir="${artifactsDir}" includes="${warName}.war"/>
<fileset dir="${projectName}_EAR/" includes="deployment.xml"/>
</ear>
Edit (2): The WebSphere Application Server Toolkit (AST) is a tool you can use to enhance an EAR file with this information (see for example the 'Configure an Enhanced EAR' section in this document).
Edit (1): This post suggests that the 'Classes loaded with application class loader first' (the PARENT_LAST setting) can be set in the deployment.xml file within the EAR.
If you have control over the automated deployment scripts this can be done. Below is some wsadmin jython code for setting the web module class loader order to 'Classes loaded with application class loader first' (interestingly the setting is called PARENT_LAST which is what it was labelled in previous versions of the admin console...).
wsadmin example (jython):
def getWebModule(config, applicationName):
webModules = config.list('WebModuleDeployment').
split(system.getProperty('line.separator'))
for webModule in webModules:
if (webModule.find(applicationName) != -1):
return webModule
return None
applicationName = "<Your application name here>"
webModule = getWebModule(AdminConfig, applicationName)
if (webModule != None):
AdminConfig.modify(webModule, "[[classloaderMode PARENT_LAST]]")
AdminConfig.save()
else:
print "Error: Cannot find web module for application: " + applicationName
Check out this link. There are different ways to set class loader policy using Jython based on your server version -
http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=%2Fcom.ibm.websphere.express.doc%2Finfo%2Fexp%2Fae%2Frxml_7libapp4.html
Similar to the answer from pkaeding, I discovered as follows, not specific to a particular .war by name, but useful when applying to whatever is the default .war in the .ear file. (.ear files with one .war file in them have only that .war, so naming the .war isn't necessary in the entry.) This approach may be good for situations where you may need to re-name of the .war project later for some reason, and so you wouldn't need to worry about updating the deployment.xml file. I found the deployment.xml file buried inside a cell reference directory trail; dunno if it's fine as shown when the file is placed at directory level META-INF and no deeper.
In my particular case, I found deployment.xml in my .ear project at:
<project_root>\META-INF\ibmconfig\cells\defaultCell\applications\defaultApp\deployments\defaultApp\
The content of the file looks a lot like:
<appdeployment:Deployment xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:appdeployment="http://www.ibm.com/websphere/appserver/schemas/5.0/appdeployment.xmi" xmi:id="Deployment_1262775196208">
<deployedObject xmi:type="appdeployment:ApplicationDeployment"
xmi:id="ApplicationDeployment_1262775196208" startingWeight="10">
<classloader xmi:id="Classloader_1262775196208" mode="PARENT_LAST" />
</deployedObject>
</appdeployment:Deployment>
The line:
<classloader xmi:id="Classloader_1262775196208" mode="PARENT_LAST" />
originally read:
<classloader xmi:id="Classloader_1262775196208" mode="PARENT_FIRST" />
Note no reference to any .war is being made. As pkaeding mentioned, you shouldn't expect the various id numbers to be the same for you.

Categories