/* AngularBeans, CDI-AngularJS bridge Copyright (c) 2014, Bessem Hmidi. or third-party contributors as indicated by * the @author tags or express copyright attribution statements applied by the authors. This copyrighted material is * made available to anyone wishing to use, modify, copy, or redistribute it subject to the terms and conditions of the * GNU Lesser General Public License, as published by the Free Software Foundation. This program is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ /** * @author Bessem Hmidi */ package angularBeans.remote; import static angularBeans.util.Accessors.*; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import org.boon.Pair; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import angularBeans.api.NGPostConstruct; import angularBeans.api.NGReturn; import angularBeans.context.BeanLocator; import angularBeans.context.NGSessionScopeContext; import angularBeans.io.ByteArrayCache; import angularBeans.io.LobWrapper; import angularBeans.log.NGLogger; import angularBeans.log.NGLogger.Level; import angularBeans.util.AngularBeansUtils; import angularBeans.util.CommonUtils; import angularBeans.util.ModelQueryFactory; import angularBeans.util.ModelQueryImpl; /** * AngularBeans RPC main handler. * * @author Bassem Hmidi */ @SuppressWarnings("serial") @ApplicationScoped public class InvocationHandler implements Serializable { @Inject ByteArrayCache cache; @Inject NGLogger logger; @Inject AngularBeansUtils util; @Inject BeanLocator locator; @Inject ModelQueryFactory modelQueryFactory; static final Map<String, Class> builtInMap = new HashMap<>(); static { builtInMap.put("int", Integer.TYPE); builtInMap.put("long", Long.TYPE); builtInMap.put("double", Double.TYPE); builtInMap.put("float", Float.TYPE); builtInMap.put("boolean", Boolean.TYPE); builtInMap.put("char", Character.TYPE); builtInMap.put("byte", Byte.TYPE); builtInMap.put("short", Short.TYPE); } public void realTimeInvoke(Object ServiceToInvoque, String methodName, JsonObject params, RealTimeDataReceivedEvent event, long reqID, String UID) { NGSessionScopeContext.setCurrentContext(UID); Map<String, Object> returns = new HashMap<>(); returns.put("isRT", true); try { genericInvoke(ServiceToInvoque, methodName, params, returns, reqID, UID,null); if (returns.get("mainReturn") != null) { event.getConnection().write(util.getJson(returns), false); } } catch (SecurityException | ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); } } public Object invoke(Object o, String method, JsonObject params, String UID,HttpServletRequest request) { NGSessionScopeContext.setCurrentContext(UID); Map<String, Object> returns = new HashMap<>(); try { returns.put("isRT", false); genericInvoke(o, method, params, returns, 0, UID,request); } catch (Exception e) { e.printStackTrace(); } return returns; } private void genericInvoke(Object service, String methodName, JsonObject params, Map<String, Object> returns, long reqID, String UID,HttpServletRequest request) throws SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { Object mainReturn = null; Method m = null; JsonElement argsElem = params.get("args"); if (reqID > 0) { returns.put("reqId", reqID); } if (argsElem != null) { JsonArray args = params.get("args").getAsJsonArray(); m = CommonUtils.getMethod(service.getClass(), methodName, args.size()); Pair<Boolean, Map<Integer, String>> ngCast = CommonUtils.getParamCastMap(m); Type[] parameters = m.getGenericParameterTypes(); if (parameters.length == args.size()) { List<Object> argsValues = new ArrayList<>(); for (int i = 0; i < parameters.length; i++) { JsonElement element = args.get(i); Type type = null; if (ngCast != null) { type = CommonUtils.getParamType(service, ngCast.getValue().get(i), ngCast.getKey()); } if (element.isJsonPrimitive()) { Class<?> clazz = null; String typeString = ((parameters[i]).toString()); if (typeString.startsWith("class")) { clazz = Class.forName(typeString.substring(6)); if (clazz.equals(Object.class)) { clazz = CommonUtils.getPrimitiveClass(element); } } else { clazz = builtInMap.get(typeString); } String val = element.getAsString(); if (type == null) { argsValues.add(CommonUtils.convertFromString(val, clazz)); } else { argsValues.add(util.deserialise(type, element)); } } else if (element.isJsonArray()) { JsonArray arr = element.getAsJsonArray(); if (type == null) { argsValues.add(util.deserialise(parameters[i], arr)); } else { argsValues.add(util.deserialise(type, arr)); } } else { if (type == null) { argsValues.add(util.deserialise(parameters[i], element)); } else { argsValues.add(util.deserialise(type, element)); } } } if (!CommonUtils.isGetter(m)) { update(service, params); } try { mainReturn = m.invoke(service, argsValues.toArray()); } catch (Exception e) { handleException(m, e); e.printStackTrace(); } } } else { m = CommonUtils.getMethod(service.getClass(), methodName, 0); // handling methods that took HttpServletRequest as parameter if (!CommonUtils.isGetter(m)) { update(service, params); } mainReturn = m.invoke(service); } ModelQueryImpl qImpl = (ModelQueryImpl) modelQueryFactory.get(service.getClass()); Map<String, Object> scMap = new HashMap<>(qImpl.getData()); returns.putAll(scMap); qImpl.getData().clear(); if (!modelQueryFactory.getRootScope().getRootScopeMap().isEmpty()) { returns.put("rootScope", new HashMap<>(modelQueryFactory.getRootScope().getRootScopeMap())); modelQueryFactory.getRootScope().getRootScopeMap().clear(); } String[] updates = null; if (m != null && m.isAnnotationPresent(NGReturn.class)) { if (mainReturn == null) mainReturn = ""; NGReturn ngReturn = m.getAnnotation(NGReturn.class); updates = ngReturn.updates(); if (ngReturn.model().length() > 0) { returns.put(ngReturn.model(), mainReturn); Map<String, String> binding = new HashMap<>(); binding.put("boundTo", ngReturn.model()); mainReturn = binding; } } if (m != null && m.isAnnotationPresent(NGPostConstruct.class)) { NGPostConstruct ngPostConstruct = m.getAnnotation(NGPostConstruct.class); updates = ngPostConstruct.updates(); } if (updates != null) { if ((updates.length == 1) && (updates[0].equals("*"))) { List<String> upd = new ArrayList<>(); for (Method met : service.getClass().getDeclaredMethods()) { if (CommonUtils.isGetter(met)) { String fieldName = (met.getName()).substring(3); String firstCar = fieldName.substring(0, 1); upd.add((firstCar.toLowerCase() + fieldName.substring(1))); } } updates = new String[upd.size()]; for (int i = 0; i < upd.size(); i++) { updates[i] = upd.get(i); } } } if (updates != null) { for (String up : updates) { String getterName = GETTER_PREFIX + up.substring(0, 1).toUpperCase() + up.substring(1); Method getter; try { getter = service.getClass().getMethod(getterName); } catch (NoSuchMethodException e) { getter = service.getClass().getMethod((getterName.replace(GETTER_PREFIX, BOOLEAN_GETTER_PREFIX))); } Object result = getter.invoke(service); returns.put(up, result); } } returns.put("mainReturn", mainReturn); if (!logger.getLogPool().isEmpty()) { returns.put("log", logger.getLogPool().toArray()); logger.getLogPool().clear(); } } private void handleException(Method m, Exception e) { Throwable cause = e.getCause(); String exceptionString = m.getName() + " -->" + cause.getClass().getName(); if (cause.getMessage() != null) { exceptionString += " " + cause.getMessage(); } logger.log(Level.ERROR, exceptionString); } private void update(Object o, JsonObject params) { if (params != null) { // boolean firstIn = false; for (Map.Entry<String, JsonElement> entry : params.entrySet()) { JsonElement value = entry.getValue(); String name = entry.getKey(); if ((name.equals("sessionUID")) || (name.equals("args"))) { continue; } if ((value.isJsonObject()) && (!value.isJsonNull())) { String getName; try { getName = CommonUtils.obtainGetter(o.getClass().getDeclaredField(name)); Method getter = o.getClass().getMethod(getName); Object subObj = getter.invoke(o); // logger.log(Level.INFO, "#entring sub object "+name); update(subObj, value.getAsJsonObject()); } catch (NoSuchFieldException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); } } // ------------------------------------ if (value.isJsonArray()) { try { String getter = CommonUtils.obtainGetter(o.getClass().getDeclaredField(name)); Method get = o.getClass().getDeclaredMethod(getter); Type type = get.getGenericReturnType(); ParameterizedType pt = (ParameterizedType) type; Type actType = pt.getActualTypeArguments()[0]; String className = actType.toString(); className = className.substring(className.indexOf("class") + 6); Class clazz = Class.forName(className); JsonArray array = value.getAsJsonArray(); Collection collection = (Collection) get.invoke(o); Object elem; for (JsonElement element : array) { if (element.isJsonPrimitive()) { JsonPrimitive primitive = element.getAsJsonPrimitive(); elem = element; if (primitive.isBoolean()) elem = primitive.getAsBoolean(); if (primitive.isString()) { elem = primitive.getAsString(); } if (primitive.isNumber()) elem = primitive.isNumber(); } else { elem = util.deserialise(clazz, element); } try { if (collection instanceof List) { if (collection.contains(elem)) collection.remove(elem); } collection.add(elem); } catch (UnsupportedOperationException e) { Logger.getLogger("AngularBeans").log(java.util.logging.Level.WARNING, "trying to modify an immutable collection : " + name); } } } catch (Exception e) { e.printStackTrace(); } } // ------------------------------------------ if (value.isJsonPrimitive() && (!name.equals("setSessionUID"))) { try { if (!CommonUtils.hasSetter(o.getClass(), name)) { continue; } name = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); Class type = null; for (Method set : o.getClass().getDeclaredMethods()) { if (CommonUtils.isSetter(set)) { if (set.getName().equals(name)) { Class<?>[] pType = set.getParameterTypes(); type = pType[0]; break; } } } if (type.equals(LobWrapper.class)) continue; Object param = null; if ((params.entrySet().size() >= 1) && (type != null)) { param = CommonUtils.convertFromString(value.getAsString(), type); } o.getClass().getMethod(name, type).invoke(o, param); } catch (Exception e) { e.printStackTrace(); } } } } } }