Не работает SNMP в Java процедуре
Пытаюсь запустить следующую Java процедуру в Oracle 19с JVM. Данная процедура должна запросить по SNMP у принтера определенный OID и вернуть его значение.
import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
public class Get_ink_levels_devices {
Snmp snmp = null;
String address = null;
/**
* Constructor
* @param add
*/
public Get_ink_levels_devices(String add)
{
address = add;
}
public static void main(String[] args) throws IOException {
/**
* Port 161 is used for Read and Other operations
* Port 162 is used for the trap generation
*/
Get_ink_levels_devices client = new Get_ink_levels_devices("udp:192.168.115.77/161");
client.start();
/**
* OID - .1.3.6.1.2.1.1.1.0 => SysDec
* OID - .1.3.6.1.2.1.1.5.0 => SysName
*.1.3.6.1.2.1.1.1.0
*.1.3.6.1.2.1.25.3.2.1.3.1
* => MIB explorer will be usefull here, as discussed in previous article
*/
String sysDescr = client.getAsString(new OID("1.3.6.1.2.1.25.3.2.1.3.1"));
System.out.println(sysDescr);
}
private void start() throws IOException {
TransportMapping transport = new DefaultUdpTransportMapping();
snmp = new Snmp(transport);
// Do not forget this line!
transport.listen();
}
/**
* Method which takes a single OID and returns the response from the agent as a String.
* @param oid
* @return
* @throws IOException
*/
public String getAsString(OID oid) throws IOException {
ResponseEvent event = get(new OID[]{oid});
if(event.getResponse() != null){
return event.getResponse().get(0).getVariable().toString();
} else {
return "no target";
}
}
/**
* This method is capable of handling multiple OIDs
* @param oids
* @return
* @throws IOException
*/
public ResponseEvent get(OID oids[]) throws IOException {
PDU pdu = new PDU();
for (OID oid : oids) {
pdu.add(new VariableBinding(oid));
}
pdu.setType(PDU.GET);
ResponseEvent event = snmp.send(pdu, getTarget(), null);
if(event != null) {
return event;
}
throw new RuntimeException("GET timed out");
}
/**
* This method returns a Target, which contains information about
* where the data should be fetched and how.
* @return
*/
private Target getTarget() {
Address targetAddress = GenericAddress.parse(address);
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
target.setAddress(targetAddress);
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version1);
return target;
}
}
После компиляции загружаю с loadjava. Вызываю так:
CREATE OR REPLACE Procedure Test_ink AS LANGUAGE JAVA NAME 'Get_ink_levels_devices.main(java.lang.String[])';
/
set serveroutput on;
call dbms_java.set_output(1000000);
exec Test_ink;
После этого она замирает, и через некоторое время выдает no target и все.
При этом в консоли возвращает значение.
Пропробовал WireShark, показывает, что пакеты приходят и уходят, но процедура почему-то не принимает ответ.
Добавил различные разрешения для Java например:
dbms_java.grant_permission (grantee => 'ADMIN',permission_type => 'java.net.SocketPermission',permission_name => 'localhost',permission_action => 'accept,connect,listen,resolve');
dbms_java.grant_permission (grantee => 'ADMIN',permission_type => 'java.net.SocketPermission',permission_name => '*',permission_action => 'accept,connect,listen,resolve');
dbms_java.grant_permission (grantee => 'ADMIN',permission_type => 'java.net.SocketPermission',permission_name => '192.168.115.77',permission_action => 'accept,connect,listen,resolve');
, но процедура не хочет правильно выполняться. То есть, при выполнении из консоли она возвращает значение OID, но при запуске в БД возвращает no target, как будто ответ не доходит.
Не могу понять, это связано с отсутствием необходимых Java разрешений или с чем-то ещё? Если дело в разрешениях, то какие нужны? Я уже разные пробовал: и java.net.NetPermission и java.net.SocketPermission.
Я правильно понимаю, что при любой нехватке разрешений будет исключение, даже если процедура отправила пакет, но не может получить ответный из-за нехватки разрешений, и должно возникнуть исключение а не просто штатное завершение программы как будто хост не доступен.Просто тогда не понятно, пакеты уходят я вижу их в WireShark, но почему то процедура не может их принять словно Oracle блокирует, не пропускает их. Собственно это и есть воспроизводимый пример, надо только скачать отсюда библиотеку www.snmp4j.org "snmp4j-2.8.3.jar" загрузить её в Oracle и после уже загружать процедуру.
Вообщем попробовал выполнить удаленную отладку данной процедуры через SQL Developer с использованием CALL DBMS_DEBUG_JDWP.CONNECT_TCP.В результате выпало сообщение
The debbuger has hit an exception breackpoint, but it cannot find the source file.Unable to find source file for package java.net,filename DualStackPlainDatagramSocketImpl.java.Project IdeConnections%23OEC.jpr and its dependencies have already been searched.
Как я понимаю он не может найти файл с пакетом java так как он встроен в саму СУБД Oracle так же было сообщение
Exception breakpoint: DualStackPlainDatagramSocketImpl.java:157, java.net.SocketException, getsockopt() failed, errno = 0
Для вызова getsockopt или setsockopt был указан неизвестный, недопустимый или неподдерживаемый параметр или уровень.
После этого я нашел в интернете, похожую процедуру
import java.io.IOException;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.smi.*;
import org.snmp4j.mp.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.util.*;
public class Collector1 {
public static void main(String[] args) throws IOException {
Snmp snmp4j = new Snmp(new DefaultUdpTransportMapping());
snmp4j.listen();
Address add = new UdpAddress("192.168.115.77/161");
CommunityTarget target = new CommunityTarget();
target.setAddress(add);
target.setTimeout(1500);
target.setRetries(3);
target.setCommunity(new OctetString("public"));
target.setVersion(SnmpConstants.version1);
PDU request = new PDU();
request.setType(PDU.GET);
OID oid= new OID("1.3.6.1.2.1.25.3.2.1.3.1");
request.add(new VariableBinding(oid));
PDU responsePDU=null;
ResponseEvent responseEvent;
responseEvent = snmp4j.send(request, target);
if (responseEvent.getResponse() != null)
{
String sysDescr = responseEvent.getResponse().get(0).getVariable().toString();
System.out.println(sysDescr);
}
else
{
System.out.println("no target");
}
}
}
Использующую туже библиотеку snmp4j-2.8.3. Тот же результат и та же ошибка при отладке SQL Developer, при этом в консоли cmd все выполняется нормально. Наконец я взял простую процедуру, которая просто отправляет UDP диаграмму на заданный адрес и порт
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class udp {
public static void main(String[] args){
System.out.println(udp.send("192.168.112.77","53","Test"));
}
public static String send(String ip,String port,String msg){
try{
int int_port=Integer.parseInt(port);
DatagramSocket ds = new DatagramSocket();
DatagramPacket dp = new DatagramPacket(msg.getBytes(), msg.length(), InetAddress.getByName(ip), int_port);
ds.send(dp);
ds.close();
return "Successfully";
}catch(Exception e){
return "Failed";
}
}
}
И в случае успеха выводит сообщение и опять таки в консоли все нормально, при выполнении в базе данных возвращает Failed. В SQL Developer опять та же ошибка. Я не разбираюсь в Java и просто пробую,но исходя из всего этого, можно ли предположить, что это какой-то баг в Oracle JVM при работе с UDP, у меня версия JDK 1.8.0_201 в Oracle или же все таки проблема в разрешениях Java, но я не представляю в каких, так как разрешил все что можно в плане работы Java с сеть.
Ответы (1 шт):
Простой пример, который отправляет UDP датаграмм, работает с внутренней JVM в БД:
create or replace and compile java source named UdpTest as
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
public class Udp {
public static void main (String[] args) {
System.out.println (">>> send " + Arrays.toString (args) + " ...");
System.out.println (Udp.send (args[0], Integer.parseInt (args[1]), args[2]));
}
public static String send (String url, int port, String msg) {
try {
DatagramSocket ds = new DatagramSocket ();
DatagramPacket dp = new DatagramPacket (msg.getBytes (), msg.length (), InetAddress.getByName (url), port);
ds.send (dp);
ds.close ();
return "Successfully";
} catch (Exception e) {
e.printStackTrace ();
return "Failure";
}
}
}
/
create or replace procedure sendUdp (url varchar2, port char, msg varchar2) as
language java name 'Udp.main (java.lang.String[])';
/
Следующие разрешения были выданы (под пользователем SYS):
exec -
dbms_java.grant_permission ('SH', 'SYS:java.net.SocketPermission', 'localhost:0', 'listen,resolve'); -
dbms_java.grant_permission ('SH', 'SYS:java.net.SocketPermission', '127.0.0.1:53', 'connect,resolve');
Важно: новые разрешения не действуют в текущей сессии. Поэтому, в новой сессии:
set serveroutput on size unlimited
exec dbms_java.set_output(10000)
exec sendUdp ('localhost', 53, 'abc')
>>> send [localhost, 53, abc] ...
Successfully
Проверено с tcpdump и в консоли:
~# tcpdump -n -ilo udp port 53
22:25:37.166568 IP 127.0.0.1.36465 > 127.0.0.1.53: domain [length 3 < 12] (invalid)
$ javac Udp.java
$ java -cp . Udp localhost 53 abc
>>> send [localhost, 53, abc] ...
Successfully