Reputation: 17
I try to connect a web application which runs on a tomcat 8 to an oracle database. Both of them run as Docker containers:
docker-compose.yml:
version: "3"
services:
appweb:
build: ./app
image: "servlet-search-app:0.1"
ports:
- "8888:8080"
links:
- appdb
environment:
- DATA_SOURCE_NAME="jdbc:oracle:thin:@appdb:1521/XE"
appdb:
build: ./db
image: "servlet-search-db:0.1"
ports:
- "49160:22"
- "1521:1521"
- "8889:8080"
Dockerfile of my oracle DB image (build: ./db):
FROM wnameless/oracle-xe-11g
ADD createUser.sql /docker-entrypoint-initdb.d/
ENV ORACLE_ALLOW_REMOTE=true
Dockerfile of the tomcat image (build: ./app)
FROM tomcat:8.0.20-jre8
COPY servlet.war /usr/local/tomcat/webapps/
COPY ojdbc14-1.0.jar /usr/local/tomcat/lib/
So the app starts up as expected but throws an exception when trying to connect to the database:
java.lang.IllegalStateException: java.sql.SQLException: Io exception: Invalid connection string format, a valid format is: "host:port:sid"
org.se.lab.ui.ControllerServlet.createConnection(ControllerServlet.java:115)
org.se.lab.ui.ControllerServlet.handleSearch(ControllerServlet.java:78)
org.se.lab.ui.ControllerServlet.doPost(ControllerServlet.java:53)
org.se.lab.ui.ControllerServlet.doGet(ControllerServlet.java:38)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Now the issue seems obvious, however when I fix the DATA_SOURCE_NAME string to:
DATA_SOURCE_NAME="jdbc:oracle:thin:@appdb:1521:XE"
I get the following exception:
java.lang.IllegalStateException: java.sql.SQLException: Listener refused the connection with the following error:
ORA-12505, TNS:listener does not currently know of SID given in connect descriptor
The Connection descriptor used by the client was:
appdb:1521:XE"
org.se.lab.ui.ControllerServlet.createConnection(ControllerServlet.java:115)
org.se.lab.ui.ControllerServlet.handleSearch(ControllerServlet.java:78)
org.se.lab.ui.ControllerServlet.doPost(ControllerServlet.java:53)
org.se.lab.ui.ControllerServlet.doGet(ControllerServlet.java:38)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Now I tried to find out which one of them should actually work. Thus, I started only the DB container:
docker build -t dbtest .
docker run -it -d --rm -p 1521:1521 --name dbtest dbtest
docker inspect dbtest | grep IPAddress
>> "IPAddress": "172.17.0.4"
Next, I try to connect with sqlplus:
sqlplus system/[email protected]:1521/XE # works
sqlplus system/[email protected]:1521:XE #ERROR: ORA-12545: Connect failed because target host or object does not exist
So what's the problem? Due to the link in the docker-compose file, the tomcat container can resolve "appdb" to the container's IP.
Here's the code which should establish the connection:
protected Connection createConnection() {
String datasource = System.getenv("DATA_SOURCE_NAME");
try {
// debug
InetAddress address = null;
try {
address = InetAddress.getByName("appdb");
System.out.println(address); // resolves in appdb/10.0.0.2
System.out.println(address.getHostAddress()); // resolves in 10.0.0.2
} catch (UnknownHostException e) {
e.printStackTrace();
}
Class.forName("oracle.jdbc.driver.OracleDriver");
return DriverManager.getConnection(datasource, "system", "oracle");
} catch (SQLException | ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
Lastly here's the tnsnames.ora file:
cat $ORACLE_HOME/network/admin/tnsnames.ora
# tnsnames.ora Network Configuration File:
XE =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = fcffb044d69d)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = XE)
)
)
EXTPROC_CONNECTION_DATA =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
)
(CONNECT_DATA =
(SID = PLSExtProc)
(PRESENTATION = RO)
)
)
Thanks!
Upvotes: 0
Views: 5749
Reputation: 17
The oracle default listener resolved the configured host to the wrong IP address:
vim $ORACLE_HOME/network/admin/listener.ora:
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /u01/app/oracle/product/11.2.0/xe)
(PROGRAM = extproc)
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
(ADDRESS = (PROTOCOL = TCP)(HOST = f4c4a3638c11)(PORT = 1521))
)
)
DEFAULT_SERVICE_LISTENER = (XE)
The HOST value is the Docker container id. If we look at /etc/hosts it is set up correctly for the service link in the docker-compose link:
10.0.0.5 f4c4a3638c11
It gets also resolved correctly from the tomcat container
ping f4c4a3638c11
PING f4c4a3638c11 (10.0.0.5): 56 data bytes
...
If I try to connect with an IP address of the other interface, which is the docker interface from the host system, the connection from the web application to the database works
String datasource = "jdbc:oracle:thin:@172.17.0.4:1521:XE";
So the solution is to configure the listener to listen to the correct IP address
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.0.5)(PORT = 1521))
Now this connection string works:
jdbc:oracle:thin:@appdb:1521:XE
I will report this behavior to wnameless/oracle-xe-11g as a bug
Upvotes: 1
Reputation: 2027
Sorry, this is not a definitive answer. Let's treat it as long comment :)
Your setup is quite complex for me to recreate however your error message is intriguing:
The Connection descriptor used by the client was:
appdb:1521:XE"
...
It looks like the environment value was chopped to appdb:1521:XE
. How about if you try hard-coding:
String datasource = "jdbc:oracle:thin:@appdb:1521/XE";
If that works, then probably need to somehow escape your docker DATA_SOURCE_NAME
environment variable.
I could be completely wrong but I think it is worth a try.
Upvotes: 0