/** * Enhances controller classes with a method missing implementation for tags at compile time. * * @author Graeme Rocher * @since 2.0 */ public class ControllerTagLibraryApi extends CommonWebApi { private static final long serialVersionUID = 1; private transient TagLibraryLookup tagLibraryLookup; private boolean developmentMode = Environment.isDevelopmentMode(); public ControllerTagLibraryApi(GrailsPluginManager pluginManager) { super(pluginManager); } public ControllerTagLibraryApi() { super(null); } @Autowired public void setTagLibraryLookup(TagLibraryLookup lookup) { tagLibraryLookup = lookup; } @Autowired public void setGspTagLibraryLookup(TagLibraryLookup lookup) { tagLibraryLookup = lookup; } /** * Method missing implementation that handles tag invocation by method name * * @param instance The instance * @param methodName The method name * @param argsObject The arguments * @return The result */ public Object methodMissing(Object instance, String methodName, Object argsObject) { Object[] args = argsObject instanceof Object[] ? (Object[]) argsObject : new Object[] {argsObject}; TagLibraryLookup lookup = getTagLibraryLookup(); if (lookup != null) { GroovyObject tagLibrary = lookup.lookupTagLibrary(GroovyPage.DEFAULT_NAMESPACE, methodName); if (tagLibrary != null) { if (!developmentMode) { MetaClass controllerMc = GrailsMetaClassUtils.getMetaClass(instance); WebMetaUtils.registerMethodMissingForTags( controllerMc, lookup, GroovyPage.DEFAULT_NAMESPACE, methodName); } List<MetaMethod> respondsTo = tagLibrary.getMetaClass().respondsTo(tagLibrary, methodName, args); if (respondsTo.size() > 0) { return respondsTo.get(0).invoke(tagLibrary, args); } } } throw new MissingMethodException(methodName, instance.getClass(), args); } /** * Looks up namespaces on missing property * * @param instance The instance * @param propertyName The property name * @return The namespace or a MissingPropertyException */ public Object propertyMissing(Object instance, String propertyName) { TagLibraryLookup lookup = getTagLibraryLookup(); NamespacedTagDispatcher namespacedTagDispatcher = lookup.lookupNamespaceDispatcher(propertyName); if (namespacedTagDispatcher != null) { if (!developmentMode) { WebMetaUtils.registerPropertyMissingForTag( GrailsMetaClassUtils.getMetaClass(instance), propertyName, namespacedTagDispatcher); } return namespacedTagDispatcher; } throw new MissingPropertyException(propertyName, instance.getClass()); } public TagLibraryLookup getTagLibraryLookup() { if (tagLibraryLookup == null) { ApplicationContext applicationContext = getApplicationContext(null); if (applicationContext != null) { try { tagLibraryLookup = applicationContext.getBean(TagLibraryLookup.class); } catch (BeansException e) { return null; } } } return tagLibraryLookup; } public Object withCodec(Object instance, Object codecInfo, Closure body) { return WithCodecHelper.withCodec(getGrailsApplication(null), codecInfo, body); } }
/** * An immutable ConverterConfiguration which chains the lookup calls for ObjectMarshallers for * performance reasons. * * @author Siegfried Puchbauer * @author Graeme Rocher * @since 1.1 */ @SuppressWarnings("rawtypes") public class ChainedConverterConfiguration<C extends Converter> implements ConverterConfiguration<C> { private List<ObjectMarshaller<C>> marshallerList; private ChainedObjectMarshaller<C> root; private final String encoding; private final Converter.CircularReferenceBehaviour circularReferenceBehaviour; private final boolean prettyPrint; private ProxyHandler proxyHandler; private final boolean cacheObjectMarshallerByClass; private Map<Integer, ObjectMarshaller<C>> objectMarshallerForClassCache; private final boolean developmentMode = Environment.isDevelopmentMode(); private final ObjectMarshaller<C> NULL_HOLDER = new ObjectMarshaller<C>() { public boolean supports(Object object) { return false; } public void marshalObject(Object object, C converter) throws ConverterException {} }; public ChainedConverterConfiguration(ConverterConfiguration<C> cfg) { this(cfg, new DefaultProxyHandler()); } public ChainedConverterConfiguration(ConverterConfiguration<C> cfg, ProxyHandler proxyHandler) { marshallerList = cfg.getOrderedObjectMarshallers(); this.proxyHandler = proxyHandler; encoding = cfg.getEncoding(); prettyPrint = cfg.isPrettyPrint(); cacheObjectMarshallerByClass = cfg.isCacheObjectMarshallerByClass(); if (cacheObjectMarshallerByClass) { objectMarshallerForClassCache = new ConcurrentHashMap<Integer, ObjectMarshaller<C>>(); } circularReferenceBehaviour = cfg.getCircularReferenceBehaviour(); List<ObjectMarshaller<C>> oms = new ArrayList<ObjectMarshaller<C>>(marshallerList); Collections.reverse(oms); ChainedObjectMarshaller<C> prev = null; for (ObjectMarshaller<C> om : oms) { prev = new ChainedObjectMarshaller<C>(om, prev); } root = prev; } public ObjectMarshaller<C> getMarshaller(Object o) { ObjectMarshaller<C> marshaller = null; Integer cacheKey = null; if (!developmentMode && cacheObjectMarshallerByClass && o != null) { cacheKey = System.identityHashCode(o.getClass()); marshaller = objectMarshallerForClassCache.get(cacheKey); if (marshaller != NULL_HOLDER && marshaller != null && !marshaller.supports(o)) { marshaller = null; } } if (marshaller == null) { marshaller = root.findMarhallerFor(o); if (cacheKey != null) { objectMarshallerForClassCache.put(cacheKey, marshaller != null ? marshaller : NULL_HOLDER); } } return marshaller != NULL_HOLDER ? marshaller : null; } public String getEncoding() { return encoding; } public Converter.CircularReferenceBehaviour getCircularReferenceBehaviour() { return circularReferenceBehaviour; } public boolean isPrettyPrint() { return prettyPrint; } public List<ObjectMarshaller<C>> getOrderedObjectMarshallers() { return marshallerList; } @SuppressWarnings("hiding") public class ChainedObjectMarshaller<C extends Converter> implements ObjectMarshaller<C> { private ObjectMarshaller<C> om; private ChainedObjectMarshaller<C> next; public ChainedObjectMarshaller(ObjectMarshaller<C> om, ChainedObjectMarshaller<C> next) { this.om = om; this.next = next; } public ObjectMarshaller<C> findMarhallerFor(Object o) { if (supports(o)) { return om; } return next != null ? next.findMarhallerFor(o) : null; } public boolean supports(Object object) { return om.supports(object); } public void marshalObject(Object object, C converter) throws ConverterException { om.marshalObject(object, converter); } } public ProxyHandler getProxyHandler() { return proxyHandler; } public boolean isCacheObjectMarshallerByClass() { return cacheObjectMarshallerByClass; } }