/* * Copyright 2013, Arondor * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.arondor.common.reflection.noreflect.instantiator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import com.arondor.common.reflection.api.instantiator.InstantiationCallback; import com.arondor.common.reflection.api.instantiator.InstantiationContext; import com.arondor.common.reflection.api.instantiator.ReflectionInstantiator; import com.arondor.common.reflection.api.instantiator.ReflectionInstantiatorAsync; import com.arondor.common.reflection.model.config.ElementConfiguration; import com.arondor.common.reflection.model.config.ListConfiguration; import com.arondor.common.reflection.model.config.MapConfiguration; import com.arondor.common.reflection.model.config.ObjectConfiguration; import com.arondor.common.reflection.model.config.PrimitiveConfiguration; import com.arondor.common.reflection.model.config.ReferenceConfiguration; import com.arondor.common.reflection.noreflect.exception.NoReflectRuntimeException; import com.arondor.common.reflection.noreflect.model.FieldSetter; import com.arondor.common.reflection.noreflect.model.ObjectConstructor; import com.arondor.common.reflection.noreflect.model.ReflectionInstantiatorCatalog; import com.arondor.common.reflection.noreflect.model.ReflectionInstantiatorRegistrar; import com.arondor.common.reflection.noreflect.runtime.AsynchronousObject; import com.arondor.common.reflection.noreflect.runtime.SimpleInstantiationContext; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; public class ReflectionInstantiatorNoReflect implements ReflectionInstantiator, ReflectionInstantiatorAsync { private static final Logger LOGGER = Logger.getLogger(ReflectionInstantiatorNoReflect.class.getName()); private static class AsyncPackages { private final List<String> packageNames = new ArrayList<String>(); private final Map<String, Boolean> packagesMap = new HashMap<String, Boolean>(); public void addPackage(String packageName) { if (!packagesMap.containsKey(packageName)) { packageNames.add(packageName); packagesMap.put(packageName, true); } } public List<String> getPackages() { return packageNames; } } @Override public InstantiationContext createDefaultInstantiationContext() { return new SimpleInstantiationContext(); } public ReflectionInstantiatorNoReflect() { if (GWT.isClient()) { ReflectionInstantiatorCatalog catalog = GWT.create(ReflectionInstantiatorCatalog.class); ReflectionInstantiatorRegistrar registrar = GWT.create(ReflectionInstantiatorRegistrar.class); registrar.register(catalog); setReflectionInstantiatorCatalog(catalog); } } private ReflectionInstantiatorCatalog reflectionInstantiatorCatalog; public final ReflectionInstantiatorCatalog getReflectionInstantiatorCatalog() { return reflectionInstantiatorCatalog; } public final void setReflectionInstantiatorCatalog(ReflectionInstantiatorCatalog reflectionInstantiatorCatalog) { this.reflectionInstantiatorCatalog = reflectionInstantiatorCatalog; } @SuppressWarnings("unchecked") private final <T> T castObject(Object object, Class<T> desiredClass) { return (T) object; } @Override public <T> T instanciateObject(ObjectConfiguration objectConfiguration, Class<T> desiredClass, InstantiationContext context) { if (objectConfiguration == null) { throw new IllegalArgumentException("Null objectConfiguration !"); } ObjectConstructor objectConstructor = reflectionInstantiatorCatalog.getObjectConstructor(objectConfiguration .getClassName()); if (objectConstructor == null) { LOGGER.log(Level.SEVERE, "No class name found : " + objectConfiguration.getClassName()); throw new IllegalArgumentException("No class name found : " + objectConfiguration.getClassName()); } List<Object> constructorArguments = instanciateObjectConstructorArguments(objectConfiguration, context); Object object = objectConstructor.create(constructorArguments); instanciateObjectFields(objectConfiguration, context, object); for (ObjectInstanciationHook hook : objectInstanciationHook) { hook.onObjectInstanciated(objectConfiguration, object); } return castObject(object, desiredClass); } private void instanciateObjectFields(ObjectConfiguration objectConfiguration, InstantiationContext context, Object object) { if (objectConfiguration.getFields() != null) { for (Map.Entry<String, ElementConfiguration> entry : objectConfiguration.getFields().entrySet()) { instanciateObjectField(object, objectConfiguration.getClassName(), entry.getKey(), entry.getValue(), context); } } } private List<Object> instanciateObjectConstructorArguments(ObjectConfiguration objectConfiguration, InstantiationContext context) { List<Object> constructorArguments = new ArrayList<Object>(); if (objectConfiguration.getConstructorArguments() != null && !objectConfiguration.getConstructorArguments().isEmpty()) { for (ElementConfiguration field : objectConfiguration.getConstructorArguments()) { constructorArguments.add(instanciateObjectField(field, context)); } } return constructorArguments; } private Object instantiateSharedObjectReference(ReferenceConfiguration referenceConfiguration, InstantiationContext context) { Object resolvedObject = context.getSharedObject(referenceConfiguration.getReferenceName()); if (resolvedObject != null) { return resolvedObject; } ObjectConfiguration resolvedConfiguration = context.getSharedObjectConfiguration(referenceConfiguration .getReferenceName()); if (resolvedConfiguration != null) { return instanciateObjectField(resolvedConfiguration, context); } throw new IllegalArgumentException("Could not resolve reference : " + referenceConfiguration.getReferenceName()); } private Object instanciateObjectField(ElementConfiguration fieldConfiguration, InstantiationContext context) { switch (fieldConfiguration.getFieldConfigurationType()) { case Primitive: { return ((PrimitiveConfiguration) fieldConfiguration).getValue(); } case Object: { return instanciateObject((ObjectConfiguration) fieldConfiguration, Object.class, context); } case List: { List<ElementConfiguration> list = ((ListConfiguration) fieldConfiguration).getListConfiguration(); if (list.isEmpty()) { return null; } List<Object> objectList = new ArrayList<Object>(); for (ElementConfiguration childFieldConfiguration : list) { objectList.add(instanciateObjectField(childFieldConfiguration, context)); } return objectList; } case Reference: { return instantiateSharedObjectReference((ReferenceConfiguration) fieldConfiguration, context); } case Map: { Map<Object, Object> objectMap = new HashMap<Object, Object>(); Map<ElementConfiguration, ElementConfiguration> mapConfiguration = ((MapConfiguration) fieldConfiguration) .getMapConfiguration(); for (Entry<ElementConfiguration, ElementConfiguration> entry : mapConfiguration.entrySet()) { Object key = instanciateObjectField(entry.getKey(), context); Object value = instanciateObjectField(entry.getValue(), context); objectMap.put(key, value); } return objectMap; } default: throw new NoReflectRuntimeException("Not implemented yet : ElementConfiguration type:" + fieldConfiguration.getFieldConfigurationType()); } } private void instanciateObjectField(final Object object, String className, String propertyName, ElementConfiguration fieldConfiguration, InstantiationContext context) { try { final FieldSetter fieldSetter = reflectionInstantiatorCatalog.getFieldSetter(className, propertyName); if (fieldSetter == null) { throw new NoReflectRuntimeException("No setter found : className:" + className + ", propertyName:" + propertyName); } Object value = instanciateObjectField(fieldConfiguration, context); if (value instanceof AsynchronousObject) { ((AsynchronousObject) value).addCallback(new AsyncCallback<Object>() { @Override public void onSuccess(Object result) { fieldSetter.set(object, result); } @Override public void onFailure(Throwable caught) { LOGGER.log(Level.SEVERE, "Could not set asynchronous object", caught); } }); } else { fieldSetter.set(object, value); } } catch (IllegalArgumentException e) { LOGGER.log(Level.SEVERE, "At " + className + ":" + propertyName); throw (e); } } public Object instanciatePrimite(String value, Class<?> targetClass) { throw new NoReflectRuntimeException("Not implemented : instanciatePrimite()"); } @Override public <T> T instanciateObject(String beanName, Class<T> desiredClass, InstantiationContext context) { T result = instanciateObjectFromShared(beanName, desiredClass, context); if (result != null) { return result; } ObjectConfiguration objectConfiguration = context.getSharedObjectConfiguration(beanName); if (objectConfiguration == null) { throw new NoReflectRuntimeException("No configuration for beanName=" + beanName + ", desiredClass=" + desiredClass.getName()); } if (objectConfiguration.isSingleton()) instanciateObjectFromShared(beanName, desiredClass, context); result = instanciateObject(objectConfiguration, desiredClass, context); mayPutSharedObject(beanName, objectConfiguration, context, result); return result; } private <T> void mayPutSharedObject(String beanName, ObjectConfiguration objectConfiguration, InstantiationContext context, T result) { if (objectConfiguration.isSingleton()) { context.putSharedObject(beanName, result); } } private <T> T instanciateObjectFromShared(String beanName, Class<T> desiredClass, InstantiationContext context) { Object sharedObject = context.getSharedObject(beanName); if (sharedObject != null) { return (T) castObject(sharedObject, desiredClass); } return null; } private void putAsyncClass(AsyncPackages asyncPackages, String clazz) { String packageName = reflectionInstantiatorCatalog.getPackageForClass(clazz); if (packageName != null) { asyncPackages.addPackage(packageName); } } private void walkElementConfigurationForClass(final InstantiationContext context, AsyncPackages asyncClasses, ElementConfiguration fieldConfiguration) { switch (fieldConfiguration.getFieldConfigurationType()) { case Primitive: { break; } case Object: { ObjectConfiguration objectConfiguration = (ObjectConfiguration) fieldConfiguration; putAsyncClass(asyncClasses, objectConfiguration.getClassName()); if (objectConfiguration.getConstructorArguments() != null) { for (ElementConfiguration arg : objectConfiguration.getConstructorArguments()) { walkElementConfigurationForClass(context, asyncClasses, arg); } } if (objectConfiguration.getFields() != null) { for (ElementConfiguration field : objectConfiguration.getFields().values()) { walkElementConfigurationForClass(context, asyncClasses, field); } } break; } case List: { List<ElementConfiguration> list = ((ListConfiguration) fieldConfiguration).getListConfiguration(); for (ElementConfiguration child : list) { walkElementConfigurationForClass(context, asyncClasses, child); } break; } case Reference: { ReferenceConfiguration ref = (ReferenceConfiguration) fieldConfiguration; if (context.getSharedObject(ref.getReferenceName()) != null) { break; } ObjectConfiguration sharedConf = context.getSharedObjectConfiguration(ref.getReferenceName()); if (sharedConf != null) { walkElementConfigurationForClass(context, asyncClasses, sharedConf); } break; } case Map: { Map<ElementConfiguration, ElementConfiguration> mapConfiguration = ((MapConfiguration) fieldConfiguration) .getMapConfiguration(); for (Entry<ElementConfiguration, ElementConfiguration> entry : mapConfiguration.entrySet()) { walkElementConfigurationForClass(context, asyncClasses, entry.getKey()); walkElementConfigurationForClass(context, asyncClasses, entry.getValue()); } break; } default: throw new NoReflectRuntimeException("Not implemented yet : ElementConfiguration type:" + fieldConfiguration.getFieldConfigurationType()); } } @Override public <T> void instanciateObject(final ObjectConfiguration objectConfiguration, final Class<T> desiredClass, final InstantiationContext context, final InstantiationCallback<T> callback) { String beanName = objectConfiguration.getObjectName(); AsyncPackages asyncPackages = new AsyncPackages(); long startWalk = System.currentTimeMillis(); walkElementConfigurationForClass(context, asyncPackages, objectConfiguration); final long endWalk = System.currentTimeMillis(); if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("walkElementConfigurationForClass() took " + (endWalk - startWalk) + "ms"); } if (asyncPackages.getPackages().isEmpty()) { T result = instanciateObject(objectConfiguration, desiredClass, context); callback.onSuccess(result); return; } List<String> asyncPackagesList = asyncPackages.getPackages(); LOGGER.info("Instantiating async bean " + beanName + ", asyncPackages are :" + asyncPackagesList); callAsyncRecursive(asyncPackagesList, 0, new AsyncCallback<Void>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(Void __void) { long endAsyncRecursive = System.currentTimeMillis(); LOGGER.info("End of asyncRecursive=" + (endAsyncRecursive - endWalk) + "ms"); T result = instanciateObject(objectConfiguration, desiredClass, context); long endInstanciateObject = System.currentTimeMillis(); LOGGER.info("instanciateObject(name=" + objectConfiguration.getObjectName() + ", class=" + objectConfiguration.getClassName() + ")" + " took:" + (endInstanciateObject - endAsyncRecursive) + "ms"); callback.onSuccess(result); } }); } @Override public <T> void instanciateObject(final String beanName, final Class<T> desiredClass, final InstantiationContext context, final InstantiationCallback<T> callback) { T result = instanciateObjectFromShared(beanName, desiredClass, context); if (result != null) { callback.onSuccess(result); return; } final ObjectConfiguration objectConfiguration = context.getSharedObjectConfiguration(beanName); instanciateObject(objectConfiguration, desiredClass, context, new InstantiationCallback<T>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(T result) { mayPutSharedObject(beanName, objectConfiguration, context, result); callback.onSuccess(result); } }); } private void callAsyncRecursive(final List<String> asyncPackages, final int index, final AsyncCallback<Void> callback) { if (index == asyncPackages.size()) { callback.onSuccess(null); return; } final String packageName = asyncPackages.get(index); final long startGetObjectConstructor = System.currentTimeMillis(); LOGGER.info("Now instantiate async :" + packageName + ", index=" + index + "/" + asyncPackages.size()); reflectionInstantiatorCatalog.getPackageInstantiator(packageName).instantiatePackage( new InstantiationCallback<Void>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(Void result) { long endGetObjectConstructor = System.currentTimeMillis(); LOGGER.info("Instantiated async :" + packageName + ", index=" + index + "/" + asyncPackages.size() + ", time=" + (endGetObjectConstructor - startGetObjectConstructor) + "ms"); callAsyncRecursive(asyncPackages, index + 1, callback); } }); } private final List<ObjectInstanciationHook> objectInstanciationHook = new ArrayList<ReflectionInstantiator.ObjectInstanciationHook>(); @Override public HookHandler addObjectInstanciationHook(final ObjectInstanciationHook hook) { if (hook == null) { LOGGER.severe("Invalid null hook provided !"); return new HookHandler() { @Override public void remove() { } }; } objectInstanciationHook.add(hook); return new HookHandler() { @Override public void remove() { objectInstanciationHook.remove(hook); } }; } }