Oracle调用Java执行Shell命令
使用Java存储过程执行Shell命令。
- Create the Java Stored Procedure
- Publish the Java call specification
- Grant Privileges
- Test It.
- Known Issues.
仅当无法通过其他方式(例如使用计划程序的外部作业)使用所需的功能时,才应将此作为最后的选择。如下文所述,您需要非常小心文件系统的授权和授予谁对此功能的访问权。如果使用不当,则可能被恶意损坏服务器上的文件或导致重大安全漏洞。
1.Create the Java Stored Procedure
首先,我们需要创建Java类来执行shell命令。
CONN test/test
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "Host" AS
import java.io.*;
public class Host {
public static void executeCommand(String command) {
try {
String[] finalCommand;
if (isWindows()) {
finalCommand = new String[4];
// Use the appropriate path for your windows version.
//finalCommand[0] = "C:\\winnt\\system32\\cmd.exe"; // Windows NT/2000
finalCommand[0] = "C:\\windows\\system32\\cmd.exe"; // Windows XP/2003
//finalCommand[0] = "C:\\windows\\syswow64\\cmd.exe"; // Windows 64-bit
finalCommand[1] = "/y";
finalCommand[2] = "/c";
finalCommand[3] = command;
}
else {
finalCommand = new String[3];
finalCommand[0] = "/bin/sh";
finalCommand[1] = "-c";
finalCommand[2] = command;
}
final Process pr = Runtime.getRuntime().exec(finalCommand);
pr.waitFor();
new Thread(new Runnable(){
public void run() {
BufferedReader br_in = null;
try {
br_in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String buff = null;
while ((buff = br_in.readLine()) != null) {
System.out.println("Process out :" + buff);
try {Thread.sleep(100); } catch(Exception e) {}
}
br_in.close();
}
catch (IOException ioe) {
System.out.println("Exception caught printing process output.");
ioe.printStackTrace();
}
finally {
try {
br_in.close();
} catch (Exception ex) {}
}
}
}).start();
new Thread(new Runnable(){
public void run() {
BufferedReader br_err = null;
try {
br_err = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
String buff = null;
while ((buff = br_err.readLine()) != null) {
System.out.println("Process err :" + buff);
try {Thread.sleep(100); } catch(Exception e) {}
}
br_err.close();
}
catch (IOException ioe) {
System.out.println("Exception caught printing process error.");
ioe.printStackTrace();
}
finally {
try {
br_err.close();
} catch (Exception ex) {}
}
}
}).start();
}
catch (Exception ex) {
System.out.println(ex.getLocalizedMessage());
}
}
public static boolean isWindows() {
if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1)
return true;
else
return false;
}
};
/
show errors java source "Host"
2.Publish the Java call specification
接下来,我们创建PL/SQL "wrapper" / PL/SQL procedure。
CREATE OR REPLACE PROCEDURE host_command (p_command IN VARCHAR2)
AS LANGUAGE JAVA
NAME 'Host.executeCommand (java.lang.String)';
/
3.Grant Privileges
在此示例中,我们授予对服务器上所有目录的访问权限。这样的操作很危险。您需要更具体地/非常谨慎地授予谁授予对此功能的访问权限。
必须使用SYS权限授予有关JServer的相关权限才能访问文件系统。在这里仅作为测试,才授予对Oracle所有者可访问的所有文件的访问权限,但实际上这是非常危险的事情。
CONN / AS SYSDBA
DECLARE
l_schema VARCHAR2(30) := 'TEST'; -- Adjust as required.
BEGIN
DBMS_JAVA.grant_permission(l_schema, 'java.io.FilePermission', '<<ALL FILES>>', 'read ,write, execute, delete');
DBMS_JAVA.grant_permission(l_schema, 'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
DBMS_JAVA.grant_permission(l_schema, 'SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
END;
/被授权者需要重新登录,授权才会有效。另外,Oracle的所有者必须具有访问所引用文件系统的权限。
4.Test It
最后,我们用Linux的命令来测试一下。
CONN test/test
SET SERVEROUTPUT ON SIZE 1000000
CALL DBMS_JAVA.SET_OUTPUT(1000000);
BEGIN
--host_command (p_command => 'move C:\test1.txt C:\test2.txt'); --windows系统
host_command (p_command => '/bin/mv /home/oracle/test1.txt /home/oracle/test2.txt'); --Linux系统
END;
/
可以使用以下DBMS_OUTPUT.get_lines过程捕获host命令的输出。
CONN test/test
SET SERVEROUTPUT ON SIZE 1000000
CALL DBMS_JAVA.SET_OUTPUT(1000000);
DECLARE
l_output DBMS_OUTPUT.chararr;
l_lines INTEGER := 1000;
BEGIN
DBMS_OUTPUT.enable(1000000);
DBMS_JAVA.set_output(1000000);
host_command('dir C:\');
--host_command('/bin/ls /home/oracle');
DBMS_OUTPUT.get_lines(l_output, l_lines);
FOR i IN 1 .. l_lines LOOP
-- Do something with the line.
-- Data in the collection - l_output(i)
DBMS_OUTPUT.put_line(l_output(i));
END LOOP;
END;
/
使用COM Automation可以达到相同的结果,但我认为这种方法更加整洁。
已知的问题
- 根据环境的不同,即使手动调用destroy()方法,该进程在执行完命令后仍可能以僵尸的形式继续运行。如果发生这种情况,则仅在会话结束时清除该过程。在正常情况下,这并不代表问题,但是当作为作业的一部分被调用时,僵尸进程仅在作业队列协调器停止时才会结束。
- 没有为操作系统标注运行任何配置文件,因此将不会设置任何环境变量。所以,您将需要使用任何可执行文件(
"ls"变为"/bin/ls")或脚本的完整路径。或者,将所有操作编写为脚本,然后在脚本内设置相关的环境变量。
有关更多信息,请参见:
从PL/SQL执行操作系统命令
转载自https://oracle-base.com/articles/8i/shell-commands-from-plsql