use std::{sync::{Arc, Mutex}, thread::sleep, time::Duration};
use juiz_sdk::anyhow;
use super::super::core_broker::CoreBrokerPtr;
use crate::{brokers::broker_ptr::BrokerPtr, prelude::*};
use crate::brokers::{broker_factory_impl::create_broker_factory_impl, BrokerFactory, CRUDBrokerHolder};
use crate::brokers::CRUDBroker;
use interprocess::local_socket::{prelude::*, traits::Stream, GenericFilePath, GenericNamespaced, ListenerOptions};
use std::io::{self, prelude::*, BufReader};
async fn on_start(broker_manifest: Value, crud_broker: Arc<Mutex<CRUDBroker>>) -> () {
match on_start_inner_tokio(broker_manifest, crud_broker).await {
Err(e) => {
log::error!("IPCBroker::on_start_inner_tokio() error: {:?}", e);
},
Ok(_) => {
}
}
}
fn extract_method_name(args: & CapsuleMap) -> JuizResult<&String> {
let err = |name: &str | anyhow::Error::from(JuizError::CapsuleDoesNotIncludeParamError{ name: name.to_owned() });
Ok(args.get_param("method_name").ok_or_else( || err("method_name") )?)
}
fn extract_class_name<'a>(args: &'a CapsuleMap) -> JuizResult<String> {
let err = |name: &str | anyhow::Error::from(JuizError::CapsuleDoesNotIncludeParamError{ name: name.to_owned() });
let class_name = args.get_param("class_name").ok_or_else( || err("class_name") )?;
Ok(class_name.to_owned())
}
fn extract_function_name<'a>(args: &'a CapsuleMap) -> JuizResult<&String> {
let err = |name: &str | anyhow::Error::from(JuizError::CapsuleDoesNotIncludeParamError{ name: name.to_owned() });
let function_name = args.get_param("function_name").ok_or_else( || err("function_name") )?;
Ok(function_name)
}
fn handle_function(crud_broker: Arc<Mutex<CRUDBroker>>, args: CapsuleMap) -> JuizResult<CapsulePtr> {
log::info!("MessengerBroker::handle_function() called");
log::trace!(" - args: CapsuleMap = {args:?}");
let class_name = extract_class_name(&args)?;
let function_name = extract_function_name(&args)?.to_owned();
let response = match extract_method_name(&args)?.as_str() {
"CREATE" => juiz_lock(&crud_broker)?.create_class(class_name.as_str(), function_name.as_str(), args),
"READ" => juiz_lock(&crud_broker)?.read_class(class_name.as_str(), function_name.as_str(), args),
"UPDATE" => juiz_lock(&crud_broker)?.update_class(class_name.as_str(), function_name.as_str(), args),
"DELETE" => juiz_lock(&crud_broker)?.delete_class(class_name.as_str(), function_name.as_str(), args),
_ => {
Err(anyhow::Error::from(JuizError::CRUDBRokerCanNotFindMethodError{method_name: "".to_owned()}))
}
};
log::trace!(" - returns: {response:?}");
response
}
fn handle_buffer_function(crud_broker: Arc<Mutex<CRUDBroker>>, conn: &mut BufReader<LocalSocketStream>, buffer: &String) -> JuizResult<()> {
let value: Value = handle_buffer(crud_broker, buffer)?;
println!("vout{:?}, vstr{:}", value, (value.to_string() + "\n"));
match conn.get_mut().write_all((value.to_string() + "\n").as_bytes()) {
Err(e) => {
log::error!("Error({e:?}) in IPCBroker::routine()");
log::trace!("IPCBrokerCore::receive_and_send() exit");
Err(anyhow::Error::from(JuizError::BrokerSendError{}))
},
Ok(()) => {
log::trace!("IPCBrokerCore collectly sent data.");
log::trace!("IPCBrokerCore::receive_and_send() exit");
Ok(())
}
}
}
fn handle_buffer(crud_broker: Arc<Mutex<CRUDBroker>>, buffer: &String) -> JuizResult<Value> {
let value: CapsuleMap = juiz_sdk::serde_json::from_str::<Value>(buffer.as_str())?.try_into()?;
let result = handle_function(crud_broker.clone(), value)?;
capsule_to_value(result)
}
async fn handle_conn(crud_broker: &Arc<Mutex<CRUDBroker>>, mut conn: interprocess::local_socket::tokio::Stream) -> io::Result<()> {
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt},
try_join,
};
let mut recver = tokio::io::BufReader::new(&conn);
let mut buffer = String::with_capacity(128);
let recv = recver.read_line(&mut buffer);
try_join!(recv)?;
let value = handle_buffer(crud_broker.clone(), &mut buffer).or_else(|_e| { Err(io::Error::new(io::ErrorKind::Other, "handle_buffer failed.")) })?;
println!("vout{:?}, vstr{:}", value, (value.to_string() + "\n"));
let vstr = (value.to_string() + "\n").to_owned();
let send = conn.write_all(vstr.as_bytes());
try_join!(send)?;
Ok(())
}
async fn on_start_inner_tokio(broker_manifest: Value, crud_broker: Arc<Mutex<CRUDBroker>>) -> JuizResult<()> {
use interprocess::local_socket::tokio::prelude::*;
log::trace!("ipc_broker::on_start_tokio() called");
let mut namespace = obj_get_str(&broker_manifest, "namespace").or::<&str>(Ok("juiz2.sock") ).unwrap().to_owned();
log::info!("ipc_broker::on_start(namespace={namespace}, {broker_manifest:?})) called");
let name = if GenericNamespaced::is_supported() {
namespace = format!("/tmp/{:}", namespace);
namespace.as_str().to_fs_name::<GenericFilePath>()?
} else {
namespace = format!("/tmp/{:}", namespace);
namespace.as_str().to_fs_name::<GenericFilePath>()?
};
log::trace!("IPBrokerCore (namespace={:?})", name);
let opts = ListenerOptions::new().name(name);
let listener = match opts.create_tokio() {
Err(e) if e.kind() == io::ErrorKind::AddrInUse => {
return Err(e.into());
}
x => x?,
};
loop {
let conn = match listener.accept().await {
Ok(c) => c,
Err(e) => {
eprintln!("There was an error with an incoming connection: {e}");
continue;
}
};
let crud = crud_broker.clone();
tokio::spawn(async move {
if let Err(e) = handle_conn(&crud, conn).await {
eprintln!("Error while handling connection: {e}");
}
});
}
}
#[allow(unused)]
async fn on_start_inner(broker_manifest: Value, crud_broker: Arc<Mutex<CRUDBroker>>) -> JuizResult<()> {
log::trace!("ipc_broker::on_start() called");
let mut namespace = obj_get_str(&broker_manifest, "namespace").or::<&str>(Ok("juiz2.sock") ).unwrap().to_owned();
log::info!("ipc_broker::on_start(namespace={namespace}, {broker_manifest:?})) called");
let buf_size = obj_get_i64(&broker_manifest, "buffer_size").or::<JuizError>(Ok(4096))? as usize;
let name = if GenericNamespaced::is_supported() {
namespace = format!("/tmp/{:}", namespace);
namespace.as_str().to_fs_name::<GenericFilePath>()?
} else {
namespace = format!("/tmp/{:}", namespace);
namespace.as_str().to_fs_name::<GenericFilePath>()?
};
log::trace!("IPBrokerCore (namespace={:?})", name);
let opts = ListenerOptions::new().name(name);
let listener = match opts.create_sync() {
Err(e) if e.kind() == io::ErrorKind::AddrInUse => {
log::error!("ipc broker: Error. Address In Use.");
return Err(anyhow::Error::from(e));
}
x => x,
}?;
fn handle_error(conn: io::Result<LocalSocketStream>) -> Option<LocalSocketStream> {
log::warn!("IPC Broker handle_error() called");
match conn {
Ok(c) => {
Some(c)
},
Err(e) => {
eprintln!("Incoming connection failed: {e}");
None
}
}
}
for conn in listener.incoming().filter_map(handle_error) {
log::info!("incomming connection detected");
conn.set_nonblocking(true)?;
let mut conn = BufReader::new(conn);
loop {
let mut buffer = String::with_capacity(buf_size);
match conn.read_line(&mut buffer) {
Err(e) => {
log::error!("Error. {:?}", e)
}
Ok (size) => {
if size == 0 {
log::info!(" - result_size: {:?}", size);
break;
}
handle_buffer_function(crud_broker.clone(), &mut conn, &buffer)?;
}
};
sleep(Duration::from_millis(10));
}
}
Ok(())
}
pub fn create_ipc_broker_factory(core_broker: CoreBrokerPtr) -> JuizResult<Arc<Mutex<dyn BrokerFactory>>> {
fn create_broker_function(core_broker: CoreBrokerPtr, manifest: Value) -> JuizResult<BrokerPtr> {
Ok(BrokerPtr::new(CRUDBrokerHolder::new("IPCBroker", "ipc", core_broker, &on_start, manifest.clone())?))
}
let manifest = jvalue!({
"type_name": "ipc"
});
create_broker_factory_impl(core_broker, manifest, create_broker_function)
}