/*
Copyright (c) [2023] [squallzhao]
fountain is licensed under APACHE LICENSE, VERSION 2.0.
You can use this software according to the terms and conditions of the APACHE LICENSE, VERSION 2.0.
You may obtain a copy of APACHE LICENSE, VERSION 2.0 at: https://www.apache.org/licenses/LICENSE-2.0
*/
package microservice.resttemplate;
import net.http.*
import std.io.*
import encoding.url.*
import std.fs.*
import encoding.json.*
import std.collection.*
import std.sync.*
import std.time.*
import microservice.trace.*
import microservice.config.*
import microservice.common.*
import microservice.registry.*
import microservice.web.server.reqres.*
import std.convert.Parsable
public open class RestTemplate {
var ers: IRegistry;
let mtxHashMap = ReentrantMutex()
var hm = HashMap<String,ArrayList<String>>()
var refer :Refer= Refer()
var readTimeout: Int64 = 3000
public init(e:IRegistry){
this.ers = e;
var r = ResourceFile.getProperty(Constant.CLIENT_REFER)
if (let Some(v) <- r){
var arr = v.split(',')
for(i in 0..arr.size){
refer.add(arr[i])
}
}
var name = ResourceFile.getProperty(Constant.SERVER_NAME)
if (let Some(v) <- name){
refer.add(v)
}
var timeout = ResourceFile.getProperty(Constant.CLIENT_READ_TIMEOUT)
if (let Some(v) <- timeout){
readTimeout = Int64.parse(v)
}
}
public func start(): RestTemplate{
refreshAllServiceSync()
Logger.info("RestTemplate start.")
return this;
}
public func getRefer(): Refer{
return refer
}
public func getServiceAddressList(service: String): Option<ArrayList<String> >{
var arr : Option<ArrayList<String> > = Option<ArrayList<String> >.None
synchronized(mtxHashMap){
arr = hm.get(service)
}
return arr;
}
private func getServiceAddress(service: String):String{
var arr : Option<ArrayList<String> > = Option<ArrayList<String> >.None
synchronized(mtxHashMap){
arr = hm.get(service)
}
if (let Some(v) <- arr){
return v.get(0).getOrThrow();
} else{
}
return "";
}
private func refreshAllServiceSync(): Unit{
for(service in refer.getRefers()){
refreshService(service)
}
spawn{
Common.sleeps(Common.discoveryInterval)
refreshAllService()
}
}
private func refreshAllService(): Unit{
//refer.dump()
for(service in refer.getRefers()){
spawn{ =>
refreshService(service)
}
}
spawn{
Common.sleeps(Common.discoveryInterval)
refreshAllService()
}
}
private func refreshService(service: String): Unit{
try{
var serviceInfo = ers.discovery(service)
var j = JsonValue.fromStr(serviceInfo)
var inst = j.asObject().get("application").getOrThrow().asObject().get("instance").getOrThrow().asArray();
var arr = ArrayList<String>()
for(i in 0..inst.size()){
var ipAddr: String = inst.get(i).getOrThrow().asObject().get("ipAddr").getOrThrow().toJsonString()
ipAddr = ipAddr[1..ipAddr.size-1]
var port = inst.get(i).getOrThrow().asObject().get("port").getOrThrow().asObject().get("$").getOrThrow().toJsonString()
arr.append(ipAddr+":"+port)
}
synchronized(mtxHashMap){
hm.put(service,arr)
}
Logger.trace("refreshService " + service +" success ")
} catch(e: Exception){
Logger.warn("refreshService " + service +" fail")
}
}
public func postForResponse(url:String, data:String): HttpResponse{
var nurl = handleService(url)
var b = ByteArrayStream()
b.write(data.toArray())
var client = ClientBuilder().build()
var req = HttpRequestBuilder().method("POST")
.url(nurl)
.header("Content-Type","application/json")
.readTimeout(readTimeout*Duration.millisecond)
.body(b)
.build()
let v = client.send(req)
return v
}
public func postForEntity(url:String, data:String): String{
let v = postForResponse(url, data)
return StringReader(v.body).readToEnd()
}
public func getForResponse(url: String, data: String): HttpResponse{
var newUrl = handleService(url)
Logger.trace("getForEntity handleService "+newUrl)
var client = ClientBuilder().build()
var req = HttpRequestBuilder().method("GET")
.url(newUrl+"?"+data)
.header("Content-Type","application/json")
.readTimeout(readTimeout*Duration.millisecond)
.build()
let v = client.send(req)
return v
}
public func getForEntity(url: String, data: String): String{
let v = getForResponse(url,data)
return StringReader(v.body).readToEnd()
}
public func getForEntity(url: String, data: String, headers: HashMap<String,String>): String{
var newUrl = handleService(url)
Logger.trace("getForEntity handleService "+newUrl)
var client = ClientBuilder().build()
var builder = HttpRequestBuilder().method("GET")
.url(newUrl+"?"+data)
.header("Content-Type","application/json")
.readTimeout(readTimeout*Duration.millisecond)
for((k,v)in headers){
builder.header(k,v)
}
var req = builder.build()
let v = client.send(req)
return StringReader(v.body).readToEnd()
}
public func handleService(url: String):String{
if (url.startsWith("http://")) {
var sub = url[7..url.size];
var serviceName = sub[0..sub.indexOf('/').getOrThrow()]
var serviceAddress = getServiceAddress(serviceName)
return url.replace(serviceName,serviceAddress)
}
return url;
}
}