项目中有个需求是数据库自动定时扫描数据库,给符合要求的用户发送推送消息,这就需要在存储过程中自动调用发送推送的程序,由于程序复杂,所以利用orcle 的pl/sql发送http请求来间接调用外部程序。
1.首先,在orcle中创建发送http请求的java代码。
java source文件夹是用来存放java方法的,里面的代码通过orale中的jdk编译,可以在存储过程中直接运行。
在文件夹中新建类HttpInvoker,其实就是普通的HttpClient发送post请求的java方法,注意开头要写create or replace and compile java source named httpinvoker as。
create or replace and compile java source named httpinvoker as
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.util.*;
import java.net.URLEncoder;
public class HttpInvoker
{
public static String sendRequest(String name, String userId,String orderNum) throws Exception{
//处理参数
String re="";
re="订单‘"+name+"’已选中你,快去接单吧";
Map p=new HashMap();
//全体发送pushtype为ALL,不写target
p.put("pushType","ALIAS");
p.put("alert",re);
p.put("target",userId);
p.put("msgCode","1005");
p.put("orderNum",orderNum);
//活动id
p.put("activityId","");
String s="";
for (Map.Entry entry : p.entrySet()) {
s+="&"+entry.getKey()+"="+entry.getValue();
}
s=s.substring(1);
//请求地址
String url="http://112.64.35.222:9002/itcast/jpush/push";
//请求参数
String param=s;
//发送post请求代码开始
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "application/json, text/javascript, */*; q=0.01");
conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
conn.setRequestProperty("Connection", "keep-alive");
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
conn.setRequestProperty("Content-Length", "80");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
OutputStreamWriter outWriter = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
out = new PrintWriter(outWriter);
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
result=e.getMessage();
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
}
2.在functions文件夹中创建调用java程序的过程
function 和procedure存储过程很像,不过function必须返回一个结果,procedure可以返回也可以不反回
create or replace function HttpInvoker(name VARCHAR2, userId VARCHAR2 ,orderNum VARCHAR2) return VARCHAR2
as language java name
--加密函数
'HttpInvoker.sendRequest (java.lang.String,java.lang.String,java.lang.String) return java.lang.String';
注意我创建的是function,所以必须返回结果,如果你创建的是procedure,则可以不返回
3.在你的存储过程代码中调用function
IV_RESULT:=HttpInvoker(orderInfo.order_name,IV_TAKER_USER_ID,orderInfo.order_num);
在需要调用的地方调用,传入参数即可
4.这时你test测试时会发现connection连接时报错,根本发送不出去,这是因为没有给用户配置权限
java.security.AccessControlException: the Permission (java.net.SocketPermission localhost:10000 listen,resolve) has not been granted to RADIUS. The PL/SQL to grant this is dbms_java.grant_permission( 'RADIUS', 'SYS:java.net.SocketPermission', 'localhost:10000', 'listen,resolve' )
解决办法:https://blog.csdn.net/lianzhang861/article/details/80618382
5.如果还不行,可能是acl的问题
定义ACL,若没有ACL,则无法访问网络。
ACL的增加过程为:建立新的acl文件,对该文件中,用户授权,对该文件中,URL和端口授权。
可查看对应的ACL信息是否添加上