Skip to content

Mukhtar.kz

  • Главная
6 февраля, 2023 / Oracle

Работа с файл сервером по SMB из Oracle

В данном примере предполагается, что у вас Oracle установлен на ОС Linux или Unix, и необходимо скопировать файл в папку «разшаренную» на сервере Windows.
Конечно можно реализовать такое на стороне клиентского приложения или на каком нибудь сервере приложений или интеграционной шине. Но моя реализация ограничивалась только в рамках Oracle с применением его заданий по расписанию (Job).
Как решение я использовал возможность выполнения Java классов в Oracle.

Libraries

Написание Java класса
В данном примере я использовал пакет jCIFS для работы по протоколу SMB с файл сервером. Свежую версию пакета jCIFS можно будет скачать с сайта разработчика https://jcifs.samba.org/.
За основу я взял пример от сюда.
Для написания класса я использовал IDE Eclipse. Можно конечно и использовать какой нибудь PL/SQL Developer, но для меня на тот момент было удобнее использовать Eclipse для синтаксической проверки и проверки зависимостей между пакетами.
Итак создаем проект и во вкладке Libraries добавляем наш jar пакет jCIFS через кнопку Add External JARs…

Создаем в нашем проекте класс «SambaTest».
В заголовке класса добавляем импорт:

import java.io.*;
import jcifs.smb.*;

Добавляем метод copyFileToSamba с возвратом текста ошибки в случае не удачного выполнения по следующему примеру:

public static String copyFileToSamba(String inNameSource, String inNameDest, String inUsr, String inPass){
String res = "OK";
try{
// Создаем объект аутентификатор
NtlmPasswordAuthentication auth =
new NtlmPasswordAuthentication("DOMAIN", inUsr, inPass);

// Читаем содержимое исходного файла
File vFileSource = new File(inNameSource);
InputStream vISFile = new FileInputStream(vFileSource);

SmbFile vSmbFile = new SmbFile(inNameDest, auth);
// Создаем объект для потока куда мы будем писать наша файл
SmbFileOutputStream vDestFile = new SmbFileOutputStream(vSmbFile);

// Ну и копируем все из исходного потока в поток назначения.
BufferedReader vBrl = new BufferedReader(new InputStreamReader(vISFile));
String b = null;
while((b=vBrl.readLine())!=null){
vDestFile.write(b.getBytes());
}
vDestFile.flush();
vBrl.close();
vDestFile.close();
}
catch(Exception e){
res = "Ошибка! Файл источник-"+inNameSource+" Файл приемник-"+inNameDest+" Текст-"+e;
}
return res;
}

Во входящих параметрах inNameSource это путь к файлу источнику на локальном сервере где установлен Oracle (пример: /home/user/test.txt). В параметре inNameDest передается файл получатель на файл сервере Windows (пример: smb://fileserver/papka/test.txt).
Учетные данные пользователя сделал как входящие в параметрах inUsr и inPass, так как не секьюрно. Их необходимо криптовать если хранить в базе данных.

Установка в Oracle
Устанавливаем jCIFS в Oracle следующей командой:

loadjava -u USER/PASSWORD@SERVER -oci8 -resolve jcifs-1.3.19.jar

При установке может ломаться следующим текстом:

errors : class jcifs/http/NetworkExplorer
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletException
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServlet
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletResponse
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletOutputStream
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletRequest
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpSession
errors : class jcifs/http/NtlmHttpFilter
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletRequest
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletResponse
ORA-29521: не удалось найти ссылочное имя javax/servlet/Filter
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletException
ORA-29521: не удалось найти ссылочное имя javax/servlet/FilterConfig
ORA-29521: не удалось найти ссылочное имя javax/servlet/FilterChain
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpSession
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletRequest
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletResponse
errors : class jcifs/http/NtlmHttpServletRequest
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletRequestWrapper
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletRequest
errors : class jcifs/http/NtlmServlet
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServlet
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletException
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletConfig
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletRequest
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletResponse
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpSession
errors : class jcifs/http/NtlmSsp
ORA-29521: не удалось найти ссылочное имя javax/servlet/ServletException
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletRequest
ORA-29521: не удалось найти ссылочное имя javax/servlet/http/HttpServletResponse
The following operations failed
class jcifs/http/NetworkExplorer: resolution
class jcifs/http/NtlmHttpFilter: resolution
class jcifs/http/NtlmHttpServletRequest: resolution
class jcifs/http/NtlmServlet: resolution
class jcifs/http/NtlmSsp: resolution
exiting : Failures occurred during processing

Решается это дополнительной установкой пакета javax.servlet-3.0.jar. Скачать его можно от сюда
Устанавливаем пакет javax.servlet-3.0.jar

loadjava -u USER/PASSWORD@SERVER -oci8 -resolve javax.servlet-3.0.jar

Затем повторяем попытку установки пакета jCIFS как описано выше.
После того как установили все необходимые пакеты можно установить созданный нами Java класс «SambaTest»

loadjava -u USER/PASSWORD@SERVER -oci8 -resolve SambaTest.java

При установке таким способом ваш Java Source будет доступен для просмотра и редактирования в Oracle.
Можно установить и скомпилироавнный пакет вашего класса

loadjava -u USER/PASSWORD@SERVER -oci8 -resolve SambaTest.class

Но имейте ввиду, что могут быть не соответствия версии Java на локале где вы компилируите класс и Oracl-ом. При несоответствии версии Java во время установки вернется ошибка ORA-29537.
Проверить версию Java в Oracle можно скриптом:

SELECT dbms_java.get_ojvm_property(PROPSTRING=>'java.version') as "java.version"
FROM dual;

java.version
----------------------
1.8.0_72

Если вы в процессе установки получите в ответ от Oracle ошибку ORA-29521, то необходимо установить пакет описанный в ошибке.

Написание PL/SQL функции
Итак, создаем в Oracle функцию «CopySmb» по следующему примеру:

function CopySmb(inNameSource in varchar2, inNameDest in varchar2, inUsr in varchar2, inPassword in varchar2) return varchar2
as language java name 'SambaTest.copyFileToSamba(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
return java.lang.String';

Ошибки и их обработка
Если при выполнении операции происходит следующая ошибка:

jcifs.smb.SmbException: Failed to connect: fileserver/XXX.XXX.XXX.XXX
jcifs.util.transport.TransportException
java.security.AccessControlException: the Permission (java.net.SocketPermission XXX.XXX.XXX.XXX:445 connect,resolve) has not been granted to USER. The PL/SQL to grant this is dbms_java.grant_permission( 'USER', 'SYS:java.net.SocketPermission', 'XXX.XXX.XXX.XXX:445', 'connect,resolve' )
at java.security.AccessControlContext.checkPermission(AccessControlContext.java)
at java.security.AccessController.checkPermission(AccessController.java)
at java.lang.SecurityManager.checkPermission(SecurityManager.java)
at oracle.aurora.rdbms.SecurityManagerImpl.checkPermission(SecurityManagerImpl.java)
at java.lang.SecurityManager.checkConnect(SecurityManager.java)
at java.net.Socket.connect(Socket.java)
at jcifs.smb.SmbTransport.negotiate(SmbTransport.java:264)
at jcifs.smb.SmbTransport.doConnect(SmbTransport.java:319)
at jcifs.util.transport.Transport.run(Transport.java:241)
at jcifs.util.transport.Transport.run(Transport.java:258)

В ошибке есть следующая подсказка

java.security.AccessControlException: the Permission (java.net.SocketPermission XXX.XXX.XXX.XXX:445 connect,resolve) has not been granted to USER. The PL/SQL to grant this is dbms_java.grant_permission( 'USER', 'SYS:java.net.SocketPermission', 'XXX.XXX.XXX.XXX:445', 'connect,resolve' )[/cc]
Ошибка говорит о том, что в таблице dba_java_policy отсутствует разрешение на соединение к серверу XXX.XXX.XXX.XXX по порту 445. Доступ может дать пользователь с правами DBA по команде: [cc lang="plsql"]dbms_java.grant_permission( 'USER', 'SYS:java.net.SocketPermission', 'XXX.XXX.XXX.XXX:445', 'connect,resolve' );

В пакете jCIFS также реализован класс SmbException для обработки ошибок сервера. Но есть одна проблема при обработки ошибок через класс SmbException. Если возникнет ошибка java.security.AccessControlException этот класс не покажет их, а просто скажет Failed to connect: fileserver/XXX.XXX.XXX.XXX, и тут могут возникнуть гадания относительно проблем на стороне файервола.
Ниже пример реализации метода обработки ошибок через класс SmbException:

public static String GetSmbErrMsg(SmbException inException, String inFileName){
String res = "";
String vFileName = inFileName.replace("/", "\\\").replace("smb:", "");
if (SmbException.NT_STATUS_OBJECT_PATH_NOT_FOUND == inException.getNtStatus()){
res = "Ошибка! Путь к файлу "+vFileName+" не существует";
}
else if(SmbException.NT_STATUS_LOGON_FAILURE == inException.getNtStatus()) {
res = "Ошибка! Ошибка авторизации";
}
else if(SmbException.NT_STATUS_ACCESS_DENIED == inException.getNtStatus()) {
res = "Ошибка! Нет доступа по пути "+vFileName;
}
else if(SmbException.NT_STATUS_OBJECT_NAME_COLLISION == inException.getNtStatus()) {
res = "Ошибка! Файл "+vFileName+" уже существует";
}
else if(SmbException.NT_STATUS_ACCOUNT_DISABLED == inException.getNtStatus()) {
res = "Ошибка! Учетная запись не активна";
}
else if(SmbException.NT_STATUS_ACCOUNT_LOCKED_OUT == inException.getNtStatus()) {
res = "Ошибка! Учетная запись заблокирована";
}
else
{
res = "Ошибка! "+inException.getLocalizedMessage()+" "+inException.getNtStatus();
}
return res;
}

В принципе вот и все. Остальные возможности jCIFS можно будет прочитать в официальной документации разработчика.
Не судите строго по написанию Java кода, я в Java новичок 🙂
Всем Спасибо!

Поделиться:

Post navigation

Next Post:

Сделать HTTPS запрос на VBS

Добавить комментарий Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Рубрики

  • Oracle
  • vbs

©2025 Mukhtar.kz - Powered by Simpleasy