How to separate application config from AP in container on Kuberetes? - java

I want to deploy a application which use an extenal config on Kubernetes. I built a spring boot application to a war file and put it into websphere liberty. Then put application.properties in /config/config ( /config is shortcut of /opt/ibm/wlp/usr/servers/defaultServer) which I want my application to use.
I write a Dockerfile as below:
FROM websphere-liberty:19.0.0.6-javaee8
USER root
RUN mkdir -p /ibank-pv && chown -R 1001:0 /ibank-pv
RUN mkdir -p /config/config/ && chown -R 1001:0 /config/config
COPY --chown=1001:0 ibank.war /config/apps/
COPY --chown=1001:0 server.xml /config
COPY --chown=1001:0 application.properties /config/config/
USER 1001
CMD ["/opt/ibm/wlp/bin/server","run","defaultServer"]
Here is my server.xml. I set ${server.config.dir}/config as a config resource folder.
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<featureManager>
<feature>javaee-8.0</feature>
</featureManager>
<basicRegistry id="basic" realm="BasicRealm">
<!-- <user name="yourUserName" password="" /> -->
</basicRegistry>
<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443" />
<applicationManager autoExpand="true"/>
<library id="configResources">
<folder dir="${server.config.dir}/config" />
</library>
<application location="ibank.war">
<classloader privateLibraryRef="configResources" />
</application>
</server>
And I add these code to read the application.properties in ${server.config.dir}/config.
public class IbankServlet extends HttpServlet {
private static final long serialVersionUID = 7526471155622776147L;
private final Properties config;
public IbankServlet() throws Exception {
InputStream is = getClass().getResourceAsStream("application.properties");
config = new Properties();
config.load(is);
}
}
However, when I start the server my application, it still read the application.properties which is in war file. What I have to do let my application use application.properties in ${server.config.dir}/config.

Is the issue that getClass().getResourceAsStream("relative-path") looks in the Java package of that specified class? That is, whatever subdirectory corresponds to the Java package of IbankServlet. In other words, is your application.properties within the WAR file buried down in a package subdirectory?
If so, you'll need to either change to getClass().getResourceAsStream("/application.properties") (with the leading slash), and move it to the classpath root in your WAR file, or have your Docker file copy it down into the correct subdirectory.
However, if your IbankServlet is actually not in a Java package at all, this probably isn't the issue.)
(But also, I admit, I'm not certain of the precedence if a file exists in both your WAR and in a "shared library".)

Related

Context.xml not recognized and not executed by Tomcat

after setting $CATALINA_HOME in this path /usr/local/cellar/tomcat/9.0.68/libexec, i put the file Context.xml with the same name as my war "warName.xml" in this path : /usr/local/Cellar/tomcat/9.0.68/libexec/work/Catalina/localhost.
here is the content of my file :
<Context>
<Resources className="org.apache.catalina.webresources.StandardRoot">
<PreResources className="org.apache.catalina.webresources.DirResourceSet"
base="/Users/user.name/Desktop/workspace/projectName/dev-resources/config" webAppMount="/WEB-INF/classes/"/>
</Resources>
</Context>
and when i deploy and launch the server i have not my dev-resources/config folder inside /WEB-INF/classes/ in my unzipped project.
the unzipped project has the following permissions :
drwxr-x---
i'am wondering if the process can not inject the configuration because of permissions problems
thanks

configuring dev and prod datasource in liberty dynamically to load based on the environment

I am running java batch(jsr352) using liberty server. The datasource configured in server.xml. I would like to load server.xml based on the region(like dev, sit, prod). How can I pass arguments to start liberty server and load the datasource dynamically
There could be possiblity with server.env file and bootstrap.properties. since new to this.. can anyone help on this.
An easy way to do this is to use variables in your server.xml like this:
<dataSource jndiName="jdbc/db2">
<jdbcDriver libraryRef="DB2JCCLib"/>
<properties.db2.jcc databaseName="${evn.db2_name}"
serverName="${env.db2_server}"
portNumber="${env.db2_port}"/>
</dataSource>
Then, you could can set the variables in your server.env like this:
db2_name=mydb
db2_server=whatever.com
db2_port=50000
Alternatively, if you use any sort of scripting to start your Liberty servers, you can export them in the bash env like this:
$ export db2_name=mydb
$ etc...
$ wlp/bin/server start myServer
If you have configuration differences that go beyond attribute values, variables may not be sufficient. For example, suppose you use an in-memory database in dev (like Derby embedded) and a more robust database in production (like DB2).
In your primary server.xml, you can include another config xml file using a variable like this:
<server>
<include location="dbconfig-${env.ENV_LOCATION}.xml"/>
<!-- rest of common config here -->
</server>
Then you can have dev-only config in dbconfig-dev.xml like this:
<server>
<dataSource id="db" jndiName="jdbc/db">
<jdbcDriver libraryRef="DerbyLib"/>
<properties.derby.embedded databaseName="memory:testdb" createDatabase="create"/>
</dataSource>
<library id="DerbyLib">
<fileset dir="/path/to/derby.jar"/>
</library>
</server>
And production-only config in dbconfig-prod.xml like this:
<server>
<dataSource id="db" jndiName="jdbc/db">
<jdbcDriver libraryRef="DB2JCCLib"/>
<properties.db2.jcc databaseName="PRODUCTION_DB"
serverName="serious.business.com"
portNumber="50000"/>
</dataSource>
<library id="DB2JCCLib">
<fileset dir="/path/to/db2.jar"/>
</library>
</server>
Then, based on which value is set for ENV_LOCATION, either dbconfig-dev.xml or dbconfig-prod.xml will be included in your primary server.xml config.

Bluemix Liberty server.xml MySQL data source configuration

I have an EJB based application that connects to MySQL database and provides web UI for update operations. It works fine when deployed to local WAS Liberty.
Here is the server.xml configuration for data source.
<library id="MySQLDriverLib">
<file name="${User-defined_JDBC_DRIVER_PATH}/mysql-connector-java-5.1.38-bin.jar"/>
</library>
<dataSource id="DefaultDataSource" type="javax.sql.ConnectionPoolDataSource" transactional="true">
<jdbcDriver libraryRef="MySQLDriverLib"/>
<properties URL="jdbc:mysql://localhost:3306/ic16_lab2434" connectionUrl="jdbc:mysql://localhost:3306/ic16_lab2434" driver="com.mysql.jdbc.Driver" driverClass="com.mysql.jdbc.Driver" metadata="mySQL" password="object00" user="root" userName="root" />
</dataSource>
<variable name="User-defined_JDBC_DRIVER_PATH" value="C:\Software\mysql-connector-java-5.1.38"/>
As you can see, it uses library for JDBC driver jar, which is specified with path to file. Obviously, this won't work if I try to deploy just EAR to Bluemix Liberty. That's why I am deploying whole server directory to make least number of changes. Yet even in this case I don't know how to properly configure JDBC driver library for data source so server picks it up. Please help.
If you want to connect to a mysql database and want to manually provide the credentials in the server.xml, you can do the following:
server.xml:
<dataSource jndiName="jdbc/TradeDataSource">
<jdbcDriver id="mysqlDriver" libraryRef="mysql-connector" />
<properties
URL="jdbc:mysql://1.2.3.4:3306/db"
password="mypassword" user="admin" />
</dataSource>
<library description="MySQL JDBC Driver" id="mysql-connector"
name="MySQL Connector">
<fileset dir="${server.config.dir}" id="mysql-connector-jar"
includes="mysql-connector-java-*.jar" />
</library>
In this example, I would put the mysql jar file in the server config directory wlp/usr/servers/defaultServer/mysql-connector-java-5.1.34-bin.jar
You can now cf push directly from the defaultServer dir
However, the liberty buildpack can automatically generate the server.xml datasource config for databases that you bind from the Bluemix catalog. For example, if I create and bind a SQLDB or ClearDB service to my Liberty application and name the service it "TradeDataSource", the buildpack will generate the config and add the right driver jar to the classpath automatically.
cf files yourappname app/wlp/usr/servers/defaultServer/server.xml
<dataSource id='mysql-TradeDataSource' jdbcDriverRef='mysql-driver' jndiName='jdbc/TradeDataSource' transactional='true' type='javax.sql.ConnectionPoolDataSource'>
<properties id='mysql-TradeDataSource-props' databaseName='${cloud.services.TradeDataSource.connection.name}' user='${cloud.services.TradeDataSource.connection.user}' password='${cloud.services.TradeDataSource.connection.password}' portNumber='${cloud.services.TradeDataSource.connection.port}' serverName='${cloud.services.TradeDataSource.connection.host}'/>
<connectionManager id='mysql-TradeDataSource-conMgr' maxPoolSize='10'/>
</dataSource>
<jdbcDriver id='mysql-driver' javax.sql.XADataSource='org.mariadb.jdbc.MySQLDataSource' javax.sql.ConnectionPoolDataSource='org.mariadb.jdbc.MySQLDataSource' libraryRef='mysql-library'/>
I can now look up the datasource using its jndi name: jdbc/TradeDataSource

How to set tomcat work at debug mode?

I deploy a application to tomcat with context xml. I want the tomcat work at debug mode, I means if I change something inside a function, like change
String a="123";
to
String a="456";
tomcat should get the change without reload the application.
The web01.xml under %CATALINA_HOME%/conf/Catalina/localhost
<?xml version='1.0' encoding='utf-8'?>
<Context docBase="d:/document/workspace3.6/web01/WebContent" path="/web01" reloadable="false" debug="1" ></Context>
but now tomcat didn't worked as I expected, if I replace the old class file with new version, I must restart tomcat to get the change.
Why tomcat didn't reload the class,How should I do to let it work as debug mode?
I am not use Eclipse now. and I don't want to set reloadable="true", this will reload the entire application if class changed.
I used tomcat5.5.23
You're actually confusing the notions of "debugging" and hot deploy. You can configure Tomcat for debug mode, and then remotely debug your application running inside tomcat such that when you add a break point in your code, the debugger will jump to that breakpoint and halt execution.
What you actually need there is having the possibility to hotdeploy an application. With tomcat, if you modify the .java files and then copy them to the working directory of tomcat, you'll get exactly what you want, namely the ability to change something in a class and have the running tomcat-deployed application take it into account without redeploying the whole application. You can automatize this by configuring your tomcat application context (either in the tomcat server.xml file or in a project specific context.xml file) for your application to have as working directory the directory where your project code gets compiled.
here's an actual example:
Let's say you have a maven project in the directory c:\myProject. You'd have source files in the c:\myProject\src, and then when compiling it you'd get the war file and an exploded directory of the war file content in the c:\myProject\target\myProject.war and respectively c:\myProject\target\myProject. Now, if you configure your tomcat such that for the myProject tomcat context, youd have the working directory configured as c:\myProject\target\myProject, then each time you modify a .java file, the .class corresponding file will be updated in the target (and now also working) dir, and tomcat will take it into account.
I've actually used such a setup to develop with tomcat, but it's not the best. First off tomcat will hotdeploy only certain modifications, such as when you modify something in the body of an existing method. Other modifications will not be taken into account, such as adding a new method - for this you have to do a full redeploy to have it taken into account.
A far better solution is to use maven with the maven jetty plugin. This thing really works as you want: any modification you do to a class of jsp file will me immediately taken into account, and visible in the running app inside jetty.
Ok, here's an actual example:
I have the cnas-war maven project. Once I build it with Maven, I get the following directory:
c:/_andrei/work/cnas/cnas-war/target\cnas-war-0.0.1-SNAPSHOT
In here I have all the stuff that normally would get packaged in the .war file, like .class files, .jsp files, .jar files etc. Effectively it's the .war file exploded.
I also have a Tomcat 5.5 specifically tailored for the deployment of this war, cleverly placed in the tomcat_cnas folder. In the Tomcat config file (conf\server.xml) I have the following:
<?xml version="1.0" encoding="utf-8"?>
<!-- Example Server Configuration File -->
<!-- Note that component elements are nested corresponding to their
parent-child relationships with each other -->
<!-- A "Server" is a singleton element that represents the entire JVM,
which may contain one or more "Service" instances. The Server
listens for a shutdown command on the indicated port.
Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" or "Loggers" at this level.
-->
<Server port="8125" shutdown="SHUTDOWN">
<!-- Comment these entries out to disable JMX MBeans support used for the
administration web application
<Listener className="org.apache.catalina.core.AprLifecycleListener" />
<Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/> -->
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<!-- Global JNDI resources -->
<GlobalNamingResources>
<!-- Test entry for demonstration purposes -->
<Environment name="simpleValue" type="java.lang.Integer"
value="30" />
<Resource auth="Container"
configurationDirectory="c:/cnas-content"
factory="com.genia.toolbox.web.jndi_config.StringContainerFactory"
name="string/activitymanagerConfigurationContainer"
type="com.genia.toolbox.web.jndi_config.StringContainer" />
<Resource name="string/activitymanagerConfigurationContainer"
auth="Container"
type="com.genia.toolbox.web.jndi_config.StringContainer"
factory="com.genia.toolbox.web.jndi_config.StringContainerFactory"
configurationDirectory="c:/cnas-content" />
</GlobalNamingResources>
<!-- Define the Tomcat Stand-Alone Service -->
<Service name="Catalina">
<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
<Connector acceptCount="100" connectionTimeout="20000"
disableUploadTimeout="true" enableLookups="false"
maxHttpHeaderSize="8192" maxSpareThreads="75" maxThreads="150"
minSpareThreads="25" port="8081" redirectPort="8443" />
<!-- Define the top level container in our container hierarchy -->
<Engine defaultHost="localhost" name="Catalina">
<!-- for activitymanager -->
<Host name="localhost" appBase="webapps" unpackWARs="true"
autoDeploy="true" xmlValidation="false"
xmlNamespaceAware="false">
<Context path="/cnas"
docBase="c:/_andrei/work/cnas/cnas-war/target/cnas-war-0.0.1-SNAPSHOT/"
workDir="c:/_andrei/work/cnas/cnas-war/target/work-cnas/">
<ResourceLink name="string/configurationContainer"
global="string/activitymanagerConfigurationContainer"
type="com.genia.toolbox.web.jndi_config.StringContainer" />
<Resource name="bean/cnasConfig" auth="Container"
type="com.genia.toolbox.projects.cnas.war.config.CnasConfig"
factory="org.apache.naming.factory.BeanFactory"
classpath="false" fileSystem="true"
applicationFileLocation="c:/cnas-content/application.properties" />
<Resource name="bean/cnasApplicationData"
auth="Container"
type="com.genia.toolbox.projects.cnas.war.config.CnasConfig"
factory="org.apache.naming.factory.BeanFactory"
classpath="false" fileSystem="true"
applicationFileLocation="c:/cnas-content/cnas_application_data.xml" />
</Context>
<!--Context docBase="C:/travail/workspace/cnas/cnas-ws-proxy/target/webapp" path="/proxy">
<Resource name="bean/params"
auth="Container"
type="fr.genia.cnas.config.Parameters"
factory="org.apache.naming.factory.BeanFactory"
log4jFile=""
serviceUrl=""
debugMode="true" >
</Resource>
</Context-->
</Host>
</Engine>
</Service>
</Server>
As you can see, in the "context" tag I have a docBase property pointing to the snapshot directory (the one where the war is exploded after maven builds it). Now, with this setup, and having this project imported into Eclipse, if I do a maven build, and then start this Tomcat, the war will be deployed and running. At this point, if I modify the content of a method in a .java file inside Eclipse (and save), then that code will be automatically taken into account by Tomcat and the application will behave differently, without any extra re-deployment. Hope this helps
How to configure Tomcat 5.5 for debug mode?
To do what you are trying to do, You would need some thing like java rebel or some thing similar I know there are some open source alternatives to do the same.

Deployment of war file on Tomcat

Is there a way to deploy a given war file on Tomcat server? I want to do this without using the web interface.
There are several ways to deploy a Tomcat webapp:
Dropping into $CATALINA_HOME/webapps, as was already mentioned.
Using your build scripts to deploy automatically via the manager interface (that comes with Tomcat). Here are the two ways
for Maven: use the tomcat plugin. You don't need to include it in pom.xml, just issue the goal mvn tomcat:deploy, the plugin is included in Maven 2. This assumes several defaults explained in the documentation, you can configure the behaviour in the pom.xml. There are other goals that let you deploy as an exploded archive etc.
for Ant: something like this:
<property name="manager.url" value="http://localhost:8080/manager"/>
<property name="manager.username" value="manager"/>
<property name="manager.password" value="foobar"/>
<!-- Task definitions -->
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"/>
<taskdef name="list" classname="org.apache.catalina.ant.ListTask"/>
<taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask"/>
<taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask"/>
<!-- goals -->
<target name="install" depends="compile" description="Install application to servlet container">
<deploy url="${manager.url}"
username="${manager.username}"
password="${manager.password}"
path="${app.path}"
localWar="file://${build.home}"/>
</target>
<target name="list" description="List installed applications on servlet container">
<list url="${manager.url}"
username="${manager.username}"
password="${manager.password}"/>
</target>
<target name="reload" depends="compile" description="Reload application on servlet container">
<reload url="${manager.url}"
username="${manager.username}"
password="${manager.password}"
path="${app.path}"/>
</target>
<target name="remove" description="Remove application on servlet container">
<undeploy url="${manager.url}"
username="${manager.username}"
password="${manager.password}"
path="${app.path}"/>
</target>
All of those will require you to have a Tomcat user configuration. It lives $CATALINA_BASE/conf/tomcat-users.xml, but since you know already how to use the web interface, I assume you know how to configure the users and passwords.
Just copy the war file into the $TOMCAT_HOME/webapps/ directory. Tomcat will deploy the war file by automatically exploding it. FYI - If you want you can make updates directly to the exploded directory, which is useful for development.
We never use the web interface, don't like it. The wars are dropped in the webapps and server.xml edited as necessary. You need to bounce it if you edit the server.xml, but the war file should be picked up automagically. We generally delete the directory expanded from the war first so there is no confusion from where the components came.
you can edit the conf/server.xml and add an entry like this pointing to your war directory
<Context path="/strutsDisplayTag"
reloadable="true"
docBase="C:\work\learn\jsp\strutsDisplayTag"
workDir="C:\work\learn\jsp\strutsDisplayTag\work" />
ELSE
you can copy your .WAR file to the webapps directory of tomcat.
The Tomcat Client Deployer Package looks to be what you need to deploy to a remote server from the command line. From the page:
This is a package which can be used to validate, compile, compress to .WAR, and deploy web applications to production or development Tomcat servers. It should be noted that this feature uses the Tomcat Manager and as such the target Tomcat server should be running.
You can also try this command-line script for managing tomcat called tomcat-manager. It requires Python, and talks to the manager application included with tomcat via HTTP. You can do stuff from a *nix shell like:
$ tomcat-manager --user=admin --password=newenglandclamchowder \
> http://localhost:8080/manager/ stop /myapp
and:
$ tomcat-manager --user=admin --password=newenglandclamchowder \
> http://localhost:8080/manager deploy /myapp ~/src/myapp/myapp.war

Categories

Resources