package org.jibeframework.core.util;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.groovy.control.CompilationFailedException;
import org.jibeframework.core.Context;
import org.jibeframework.core.annotation.Service;
import org.jibeframework.core.annotation.UIComponent;
import org.jibeframework.core.annotation.UIController;
import org.jibeframework.core.ui.UIComponentMixin;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scripting.ScriptCompilationException;
import org.springframework.scripting.ScriptFactory;
import org.springframework.scripting.ScriptSource;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@SuppressWarnings("unchecked")
public class ViewComponentFactory implements ScriptFactory, BeanFactoryAware, ApplicationContextAware,
BeanClassLoaderAware {
private File file;
private long lastModified = -1;
private GroovyClassLoader groovyClassLoader;
private ApplicationContext applicationContext;
private AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
private Class scriptClass;
private String scriptSourceLocator;
private Map<Field, Object> injectionCache = new HashMap<Field, Object>();
private final Object scriptClassMonitor = new Object();
private ViewComponentFactory(File scriptSource) {
this.scriptSourceLocator = "file:" + scriptSource.getAbsolutePath();
file = scriptSource;
}
public Object getScriptedObject(ScriptSource scriptSource, Class[] actualInterfaces) throws IOException,
ScriptCompilationException {
if (file.lastModified() != lastModified) {
compileScriptClass(scriptSource);
}
try {
GroovyObject goo = (GroovyObject) scriptClass.newInstance();
UIComponent ann = AnnotationUtils.findAnnotation(scriptClass, UIComponent.class);
if (ann != null) {
UIComponentMixin.applyMixin(ann.value(), goo, applicationContext);
}
Context context = Context.getCurrentContext();
for (Map.Entry<Field, Object> f : injectionCache.entrySet()) {
ReflectionUtils.makeAccessible(f.getKey());
Object dependency = null;
if (f.getValue() instanceof String) {
String beanId = (String) f.getValue();
if (context.containsViewBean(beanId)) {
dependency = context.getViewBean(beanId);
} else {
dependency = applicationContext.getBean(beanId);
}
} else {
dependency = applicationContext.getBean((Class) f.getValue());
}
ReflectionUtils.setField(f.getKey(), goo, dependency);
}
processor.processInjection(goo);
return goo;
} catch (InstantiationException ex) {
throw new ScriptCompilationException(scriptSource, "Could not instantiate Groovy script class: "
+ scriptClass.getName(), ex);
} catch (IllegalAccessException ex) {
throw new ScriptCompilationException(scriptSource, "Could not access Groovy script constructor: "
+ scriptClass.getName(), ex);
}
}
public Class getScriptedObjectType(ScriptSource scriptSource) throws IOException, ScriptCompilationException {
if (file.lastModified() != lastModified) {
compileScriptClass(scriptSource);
}
return this.scriptClass;
}
private void compileScriptClass(ScriptSource scriptSource) throws IOException {
try {
synchronized (this.scriptClassMonitor) {
if (file.lastModified() != lastModified) {
this.scriptClass = getGroovyClassLoader().parseClass(scriptSource.getScriptAsString(),
scriptSource.suggestedClassName());
this.lastModified = file.lastModified();
injectionCache.clear();
Class<?> targetClass = this.scriptClass;
while (targetClass != null && targetClass != Object.class) {
for (Field field : targetClass.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
UIComponent dann = field.getAnnotation(UIComponent.class);
if (dann != null) {
injectionCache.put(field, dann.value());
} else {
UIController cann = field.getAnnotation(UIController.class);
if (cann != null) {
injectionCache.put(field, cann.value());
} else {
Service sann = field.getAnnotation(Service.class);
if (sann != null) {
injectionCache.put(field, field.getType());
}
}
}
}
}
targetClass = targetClass.getSuperclass();
}
}
}
} catch (CompilationFailedException ex) {
throw new ScriptCompilationException(scriptSource, ex);
}
}
/**
* Return the GroovyClassLoader used by this script factory.
*/
public GroovyClassLoader getGroovyClassLoader() {
synchronized (this.scriptClassMonitor) {
if (this.groovyClassLoader == null) {
this.groovyClassLoader = new GroovyClassLoader(ClassUtils.getDefaultClassLoader());
}
return this.groovyClassLoader;
}
}
public Class[] getScriptInterfaces() {
return null;
}
public String getScriptSourceLocator() {
return scriptSourceLocator;
}
public boolean requiresConfigInterface() {
return false;
}
public boolean requiresScriptedObjectRefresh(ScriptSource scriptSource) {
return false;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.groovyClassLoader = new GroovyClassLoader(classLoader);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
processor.setBeanFactory(beanFactory);
}
}