martes, 21 de mayo de 2013

Network Adapter Could not Establish Connection

Este es un escenario curioso: tienes un programa en java que se conecta a una base de datos Oracle usando direcciones IP, pero no se puede conectar a la base de datos aún si usas exactamente la misma cadena de conexión en tnsnames y puedes conectarte a esa base de datos usando el cliente SQL/Plus.

Pero empecemos desde el principio, cuando no tienes ningún problema (porque el nombre del servidor de bases de datos Oracle tiene un nombre de dominio calificado y se puede resolver):

oracle@test:~$ cd $ORACLE_HOME/network/admin
oracle@test:~/app/oracle/product/10.2.0/server/network/admin$ cat tnsnames.ora 
# tnsnames.ora Network Configuration File:

XE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = hera.localdomain)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  )

XE2 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.108.76)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = SHARED)
      (SERVICE_NAME = XE)
    )
  )

XE3 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.108.76)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  )

Como puedes ver, hay tres diferentes cadenas de conexión definidas en este archivo tnsnames: una con un FQDN como conexión dedicada, una con una dirección IP como conexión compartida, y la última con una dirección IP como conexión dedicada. Ahora pruebas el nombre del servidor e intentas conectarte usando estos tres servicios:

oracle@test:~$ ping hera
PING hera.localdomain (192.168.108.76) 56(84) bytes of data.
64 bytes from hera.localdomain (192.168.108.76): icmp_req=1 ttl=64 time=8.34 ms
64 bytes from hera.localdomain (192.168.108.76): icmp_req=2 ttl=64 time=3.58 ms

--- hera.localdomain ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1003ms
rtt min/avg/max/mdev = 3.589/5.964/8.340/2.376 ms

oracle@test:~$ sqlplus hr/hr@xe

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 15 15:44:16 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> exit
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

oracle@test:~$ sqlplus hr/hr@xe2

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 15 15:44:21 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> exit
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

oracle@test:~$ sqlplus hr/hr@xe3

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 15 15:44:26 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> exit
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

Todo estuvo bien como se esperaba, y aún este diminuto y util programa de prueba de java está trabajando:

oracle@test:~$ cat OracleJdbcExample.java |grep jdbc
        String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.108.76)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)))";

oracle@test:~$ javac -g OracleJdbcExample.java 
oracle@test:~$ export CLASSPATH=$CLASSPATH:/usr/lib/oracle/MyJDBC/ojdbc6.jar
oracle@test:~$ java OracleJdbcExample
Current Date from Oracle : 2013-05-15 16:06:08
done

Pero que pasa cuando el nombre del servidor ya no se puede resolver? Como cuando se comentan las líneas de nameserver en /etc/resolv.conf:

oracle@test:/etc# ping hera
ping: unknown host hera

oracle@test:~$ sqlplus hr/hr@xe

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 15 16:02:25 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

ERROR:
ORA-12560: TNS:protocol adapter error


Enter user-name: 

oracle@test:~$ sqlplus hr/hr@xe2

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 15 16:02:44 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> exit
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

oracle@test:~$ sqlplus hr/hr@xe3

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 15 16:02:50 2013

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> exit
Disconnected from Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

Como se esperaba, la primer cadena de conexión que usa el nombre del servidor no esta funcionando, pero las otras dos que usan direcciones IP aún funcionan. Por lo tanto, el programa de java tiene que funcionar también, correcto?

oracle@test:~$ java OracleJdbcExample
Exception in thread "main" java.sql.SQLRecoverableException: IO Error: Connection reset
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:421)
 at oracle.jdbc.driver.PhysicalConnection.(PhysicalConnection.java:531)
 at oracle.jdbc.driver.T4CConnection.(T4CConnection.java:221)
 at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
 at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:503)
 at java.sql.DriverManager.getConnection(DriverManager.java:582)
 at java.sql.DriverManager.getConnection(DriverManager.java:154)
 at OracleJdbcExample.main(OracleJdbcExample.java:25)
Caused by: java.net.SocketException: Connection reset
 at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:96)
 at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
 at oracle.net.ns.DataPacket.send(DataPacket.java:199)
 at oracle.net.ns.NetOutputStream.flush(NetOutputStream.java:211)
 at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:227)
 at oracle.net.ns.NetInputStream.read(NetInputStream.java:175)
 at oracle.net.ns.NetInputStream.read(NetInputStream.java:100)
 at oracle.net.ns.NetInputStream.read(NetInputStream.java:85)
 at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:122)
 at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:78)
 at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1179)
 at oracle.jdbc.driver.T4CMAREngine.unmarshalSB1(T4CMAREngine.java:1155)
 at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:279)
 at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
 at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:366)
 at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:752)
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:359)
 ... 7 more

Ese no es el caso. De acuerdo a la nota de Metalink de Oracle 139775.1, cuando tratas de conectarte a un servicio de Oracle por medio de una conexión dedicada usando java, la cadena de conexión tiene direcciones IP en lugar de nombres de dominio calificados, y esas direcciones IP no se pueden resolver en la máquina cliente, obtendrás un mensaje de error Network Adapter Could not Establish Connection, aún si desde ese cliente puedes alcanzar la base de datos usando un cliente SQL/Plus. En este ejemplo el mensaje de error es diferente, supongo que porque estoy usando Oracle 10g XE.

Más información:

Io exception: The Network Adapter could not establish the connection
NL Exception trying to connect to 10g RAC w/JDBC