package ddth.dasp.framework.model; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import ddth.dasp.framework.url.IUrlCreator; /** * View Model object: to be used on view. Use case: A view model object wraps a * business object and delegate only "get" method calls to the underlying * object. * * Use this class as starting point for model objects. * * @author NBThanh <btnguyen2k@gmail.com> * @version 0.1.0 */ public class BaseViewModel<T> implements InvocationHandler { private final static Pattern PATTERN_METHOD_GET = Pattern.compile("^get[A-Z]\\w*$"); private T obj; private HttpServletRequest request; private HttpServletResponse response; private IUrlCreator urlCreator; private List<Object> optionalAttrs = new LinkedList<Object>(); private static <T> BaseViewModel<T> initModelObj(BaseViewModel<T> model, HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, Object... optionalAttrs) { model.setHttpRequest(request); model.setHttpResponse(response); model.setUrlCreator(urlCreator); if (optionalAttrs != null) { for (Object obj : optionalAttrs) { model.optionalAttrs.add(obj); } } model.init(); return model; } protected void init() { // EMPTY } /** * Creates view model without creating the proxy object. * * @param request * @param response * @param urlCreator * @param obj * @param optionalAttrs * @return */ public static <T> BaseViewModel<T> createModelNoProxy(HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, T obj, Object... optionalAttrs) { BaseViewModel<T> model = new BaseViewModel<T>(obj); return initModelObj(model, request, response, urlCreator, optionalAttrs); } /** * Creates list of view model without creating the proxy objects. * * @param request * @param response * @param urlCreator * @param objs * @param optionalAttrs * @return */ @SuppressWarnings("unchecked") public static <T> BaseViewModel<T>[] createModelNoProxy(HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, T[] objs, Object... optionalAttrs) { List<BaseViewModel<T>> result = new ArrayList<BaseViewModel<T>>(); for (T obj : objs) { BaseViewModel<T> t = createModelNoProxy(request, response, urlCreator, obj, optionalAttrs); if (t != null) { result.add(t); } } return result.toArray(new BaseViewModel[0]); } /** * Creates view model and returns the proxy object. The proxy object uses * the target object's {@link ClassLoader}, and implements interfaces * returned by target object's <code>getClass().getInterfaces()</code> call. * * @param request * @param response * @param urlCreator * @param obj * @param optionalAttrs * @return */ @SuppressWarnings("unchecked") public static <T> T createModel(HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, T obj, Object... optionalAttrs) { BaseViewModel<T> model = createModelNoProxy(request, response, urlCreator, obj, optionalAttrs); return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass() .getInterfaces(), model); } /** * Creates list of view models and return the list of proxy objects. Proxy * objects use the target object's {@link ClassLoader}, and implement * interfaces returned by target object's * <code>getClass().getInterfaces()</code> call. * * @param request * @param response * @param urlCreator * @param objs * @param optionalAttrs * @return */ public static <T> Object[] createModel(HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, T[] objs, Object... optionalAttrs) { List<T> result = new ArrayList<T>(); for (T obj : objs) { T t = createModel(request, response, urlCreator, obj, optionalAttrs); if (t != null) { result.add(t); } } return result.toArray(); } @SuppressWarnings("unchecked") public static <T> T createModel(HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, Class<? extends BaseViewModel<T>> modelClazz, Class<?> targetClass, T obj, Object... optionalAttrs) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { // interfaces that the proxy will implement List<Class<?>> proxyInterfaceList = new ArrayList<Class<?>>(); // add interfaces that the target class implements/extends Class<?>[] interfacesTargetClass = targetClass.isInterface() ? new Class<?>[] { targetClass } : targetClass.getInterfaces(); for (Class<?> interf : interfacesTargetClass) { if (!proxyInterfaceList.contains(interf)) { proxyInterfaceList.add(interf); } } // add interfaces that the model class implements/extends Class<?>[] interfacesModel = modelClazz.isInterface() ? new Class<?>[] { modelClazz } : modelClazz.getInterfaces(); for (Class<?> interf : interfacesModel) { if (!proxyInterfaceList.contains(interf)) { proxyInterfaceList.add(interf); } } // add interface that the model object implements Class<?>[] interfacesTarget = obj.getClass().getInterfaces(); for (Class<?> interf : interfacesTarget) { if (!proxyInterfaceList.contains(interf)) { proxyInterfaceList.add(interf); } } // create and initialize the model object Constructor<BaseViewModel<T>> c = (Constructor<BaseViewModel<T>>) modelClazz .getDeclaredConstructor(targetClass); c.setAccessible(true); BaseViewModel<T> model = c.newInstance(obj); initModelObj(model, request, response, urlCreator, optionalAttrs); // create a combined class loader for the proxy to use. CombinedClassLoader combinedClassLoader = new CombinedClassLoader(); for (Class<?> clazz : proxyInterfaceList) { combinedClassLoader.addLoader(clazz); } return (T) Proxy.newProxyInstance(combinedClassLoader, proxyInterfaceList.toArray(new Class[0]), model); // return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), // proxyInterfaceList.toArray(new Class[0]), model); } public static <T> Object[] createModel(HttpServletRequest request, HttpServletResponse response, IUrlCreator urlCreator, Class<? extends BaseViewModel<T>> modelClazz, Class<?> targetClass, T[] objs, Object... optionalAttrs) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { List<T> result = new ArrayList<T>(); for (T obj : objs) { T t = createModel(request, response, urlCreator, modelClazz, targetClass, obj, optionalAttrs); if (t != null) { result.add(t); } } return result.toArray(); } protected BaseViewModel(T obj) { setTargetObject(obj); } protected void setTargetObject(T obj) { this.obj = obj; } protected T getTargetObject() { return obj; } protected void setHttpRequest(HttpServletRequest request) { this.request = request; } protected HttpServletRequest getHttpRequest() { return request; } protected void setHttpResponse(HttpServletResponse response) { this.response = response; } protected HttpServletResponse getHttpResponse() { return response; } protected void setUrlCreator(IUrlCreator urlCreator) { this.urlCreator = urlCreator; } protected IUrlCreator getUrlCreator() { return urlCreator; } @SuppressWarnings("unchecked") protected <K> K getOptionalAttr(Class<K> clazz) { for (Object obj : optionalAttrs) { if (clazz.isAssignableFrom(obj.getClass())) { return (K) obj; } } return null; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (!"toString".equals(methodName) && "hashCode".equals(methodName) & !PATTERN_METHOD_GET.matcher(methodName).matches()) { throw new IllegalAccessException("Invoking method [" + methodName + "] is not allowed!"); } Method m = null; try { m = this.getClass().getMethod(methodName); } catch (NoSuchMethodException e) { } if (m != null) { return m.invoke(this); } return method.invoke(getTargetObject(), args); } }