ZabbixApiClient.java
package eu.linuxengineering.zabbix;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import eu.javaexperience.asserts.AssertArgument;
import eu.javaexperience.collection.map.SmallMap;
import eu.javaexperience.datareprez.DataArray;
import eu.javaexperience.datareprez.DataObject;
import eu.javaexperience.datareprez.DataReprezTools;
import eu.javaexperience.datareprez.convertFrom.ModifiableObject;
import eu.javaexperience.datareprez.jsonImpl.DataObjectJsonImpl;
import eu.javaexperience.generic.annotations.Ignore;
import eu.javaexperience.interfaces.simple.getBy.GetBy3;
import eu.javaexperience.io.IOTools;
import eu.javaexperience.pdw.BeanTools;
import eu.javaexperience.pdw.ProxyDataWrapperTools;
import eu.javaexperience.reflect.Mirror;
import eu.javaexperience.rpc.bidirectional.BidirectionalRpcDefaultProtocol;
import eu.javaexperience.url.UrlTools;
import eu.linuxengineering.zabbix.annotation.ExtraParameters;
import eu.linuxengineering.zabbix.annotation.ParameterName;
import eu.linuxengineering.zabbix.api.TargetObject;
import eu.linuxengineering.zabbix.api.ZabbixApi;
import eu.linuxengineering.zabbix.api.ZabbixApiUser.ZabixUserData;
import eu.linuxengineering.zabbix.api.ZabbixGenericApiNode;
import eu.linuxengineering.zabbix.api.ZabbixParameters;
import eu.linuxengineering.zabbix.api.ZabbixRoot;
public class ZabbixApiClient implements Closeable
{
protected String url;
protected String sessionId;
protected ZabbixRoot root;
public ZabbixApiClient(String url)
{
this.url = url;
this.root = ProxyDataWrapperTools.wrapAccessor(new ZabbixGenericApiNode(this, ""), ZabbixRoot.class, MAPPER);
}
public static final GetBy3<Object, ZabbixGenericApiNode, Method, Object[]> MAPPER = new GetBy3<Object, ZabbixGenericApiNode, Method, Object[]>()
{
@Override
public Object getBy(ZabbixGenericApiNode path, Method b, Object[] c)
{
String methodName = b.getName();
try
{
//top interface functions:
if(BACKEND_IMPL_METHOD_NAMES.contains(methodName))
{
return relayApiCall(path, b, c);
}
switch (methodName)
{
//object's useful functions
case "toString":return "Proxy generated Zabbix api node path: "+path.getApiPath();
//interited from object
case "clone":
case "notifyAll":
case "notify":
case "wait":
case "equals":
case "hashCode":
throw new RuntimeException("illegal operation on a proxy object: "+methodName);
}
String reqName = BeanTools.getCLikeBeanName(b);
Class<?> retType = b.getReturnType();
if(ZabbixApi.class.isAssignableFrom(retType))
{
return wrapWithClass((Class) retType, path.nextPath(reqName));
}
//execute RPC call
return executeRpcCall(path, b, c);
}
catch(Exception e)
{
Mirror.propagateAnyway(e);
return null;
}
}
};
public static Object executeRpcCall(ZabbixGenericApiNode path, Method b, Object[] c) throws Exception
{
String method = path.getApiPath()+"."+b.getName();
DataObject to = DataObjectJsonImpl.instane.newObjectInstance();
to.putString("jsonrpc", "2.0");
to.putString("method", method);
to.putString("auth", path.getApiClient().sessionId);
to.putString("id", "1");
DataObject ps = to.newObjectInstance();
Parameter[] params = b.getParameters();
if(1 == params.length && ZabbixParameters.class.isAssignableFrom(params[0].getType()))
{
ZabbixParameters zps = (ZabbixParameters) c[0];
for(String k:zps.keys())
{
Object val = zps.get(k);
if(null != val)
{
DataReprezTools.put(BidirectionalRpcDefaultProtocol.DEFAULT_RPC_DATA_WRAPPER, ps, k, val);
}
}
}
else
{
for(int i=0;i<params.length;++i)
{
Parameter param = params[i];
ParameterName pn = param.getAnnotation(ParameterName.class);
if(null == pn)
{
throw new RuntimeException("No parameter name present at method signature `"+b+"` at parameter index `"+i+"`");
}
DataReprezTools.put(BidirectionalRpcDefaultProtocol.DEFAULT_RPC_DATA_WRAPPER, ps, pn.name(), c[i]);
}
}
ExtraParameters ep = b.getAnnotation(ExtraParameters.class);
if(null != ep)
{
Map<String, String[]> p = new SmallMap<>();
UrlTools.processArgsRequest(ep.params(), p);
Map<String, String> map = UrlTools.convMapMulti(p);
for(Entry<String, String> kv:map.entrySet())
{
ps.putString(kv.getKey(), kv.getValue());
}
}
to.putObject("params", ps);
URL u = new URL(path.getApiClient().url);
byte[] rj = post(u, to.getImpl().toString().getBytes());
//return as array
DataObject result = null;
try
{
result = to.objectFromBlob(rj);
}
catch(Exception e)
{
throw new RuntimeException("Exception while parsing result: "+new String(rj), e);
}
if(result.has("error"))
{
throw new RuntimeException("Error while calling `"+method+"`: "+result.getImpl());
}
Class<?> ret = b.getReturnType();
if(List.class.isAssignableFrom(ret))
{
DataArray arr = result.getArray("result");
Type genRet = b.getGenericReturnType();
if(genRet instanceof ParameterizedType)
{
Type[] args = ((ParameterizedType)genRet).getActualTypeArguments();
if(args.length > 0)
{
Type t = args[0];
if(t instanceof Class && ModifiableObject.class.isAssignableFrom(((Class)t)))
{
ArrayList ar = new ArrayList<>();
Class target = (Class) t;
for(int i=0;i<arr.size();++i)
{
try
{
ModifiableObject dst = (ModifiableObject) target.newInstance();
DataReprezTools.copyInto(dst, arr.getObject(i));
ar.add(dst);
}
catch(Exception e)
{
e.printStackTrace();
}
}
return ar;
}
else
{
throw new RuntimeException("The generic parameter of List returing type at method `"+b+"` doesn't extends "+TargetObject.class.getCanonicalName());
}
}
}
}
if(ModifiableObject.class.isAssignableFrom(ret))
{
ModifiableObject dst = (ModifiableObject) ret.newInstance();
DataReprezTools.copyInto(dst, result.getObject("result"));
return dst;
}
return result.get("result");
}
public static <T extends ZabbixApi> T wrapWithClass(Class<T> cls, ZabbixGenericApiNode zkp) throws Exception
{
if(cls.isInterface())
{
return ProxyDataWrapperTools.wrapAccessor(zkp, cls, MAPPER);
}
else
{
Constructor<?> constr = cls.getConstructor(ZabbixApiClient.class);
if(null == constr)
{
throw new RuntimeException("No constructor "+cls.getSimpleName()+"(ZookeeperPath zkp) defined in the requested class.");
}
return (T) constr.newInstance(zkp);
}
}
public static byte[] post(URL url, byte[] POST_data) throws IOException
{
URLConnection connection = url.openConnection();
if(null != POST_data)
{
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Content-Length", String.valueOf(POST_data.length));
connection.setDoOutput(true);
try
(
OutputStream os = connection.getOutputStream();
)
{
if(null != POST_data)
{
os.write(POST_data);
os.flush();
}
}
}
return IOTools.loadAllFromInputStream(connection.getInputStream());
}
protected static final Set<String> BACKEND_IMPL_METHOD_NAMES = new HashSet<>();
static
{
for(Method m:ZabbixApi.class.getDeclaredMethods())
{
if(null == m.getAnnotation(Ignore.class))
{
BACKEND_IMPL_METHOD_NAMES.add(m.getName());
}
}
}
protected static Object relayApiCall(ZabbixGenericApiNode path, Method b, Object[] c) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
return ZabbixGenericApiNode.class.getDeclaredMethod(b.getName(), b.getParameterTypes()).invoke(path, c);
}
public ZabixUserData login(String user, String password)
{
ZabixUserData ret = root.getUser().login(user, password);
sessionId = ret.sessionid;
return ret;
}
@Override
public void close() throws IOException
{
logout();
}
public void logout()
{
root.getUser().logout();
}
public ZabbixRoot getApiRoot()
{
return root;
}
}