/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.component.wiki.internal; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.component.descriptor.ComponentDescriptor; import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; import org.xwiki.component.util.ReflectionUtils; import org.xwiki.component.wiki.WikiComponent; import org.xwiki.component.wiki.WikiComponentRuntimeException; import org.xwiki.rendering.block.XDOM; /** * Method invocation handler for wiki component proxy instances. Has a reference on a map of name/body wiki code of * supported methods. * * @version $Id: 53ed15cc3861dad8569e69c20278f2857a6e2b1e $ * @since 4.2M3 */ public class DefaultWikiComponentInvocationHandler implements InvocationHandler { /** * The key under which the component reference (a virtual "this") is kept in the method invocation context. */ private static final String METHOD_CONTEXT_COMPONENT_KEY = "component"; /** * The logger to log. */ private final Logger logger = LoggerFactory.getLogger(DefaultWikiComponentInvocationHandler.class); /** * Our component manager. */ private ComponentManager componentManager; /** * The proxied wiki component. */ private DefaultWikiComponent wikiComponent; /** * Constructor of this invocation handler. * * @param wikiComponent the proxied wiki component * @param componentManager the component manager */ public DefaultWikiComponentInvocationHandler(DefaultWikiComponent wikiComponent, ComponentManager componentManager) { this.wikiComponent = wikiComponent; this.componentManager = componentManager; } /** * Retrieves the wiki component dependencies from the component manager and puts them in the method context under * the configured key. * * @param methodContext The context where the dependencies must be injected */ private void injectComponentDependencies(Map<String, Object> methodContext) { for (Map.Entry<String, ComponentDescriptor> dependency : this.wikiComponent.getDependencies().entrySet()) { ComponentDescriptor cd = dependency.getValue(); Class<?> roleTypeClass = ReflectionUtils.getTypeClass(cd.getRoleType()); Object componentDependency = null; try { if (roleTypeClass.isAssignableFrom(List.class)) { // If the ParameterizedType is a List, the raw Type is the List Class and the first Type argument // is the actual component (which can be a ParameterizedType itself). // Example: java.util.List<org.xwiki.model.reference.EntityReferenceSerializer<java.lang.String>> // raw Type: java.util.List // Type arguments [0]: org.xwiki.model.reference.EntityReferenceSerializer<java.lang.String> componentDependency = componentManager.getInstanceList( ((ParameterizedType) cd.getRoleType()).getActualTypeArguments()[0]); } else if (roleTypeClass.isAssignableFrom(Map.class)) { // If the ParameterizedType is a Map, the raw Type is the Map, the first argument can only be a // String in our implementation and the second argument is the actual component. // Example: java.util.Map<java.lang.String, // org.xwiki.model.reference.EntityReferenceSerializer<java.lang.String>> // raw Type: java.util.Map // Type arguments [0]: java.lang.String // [1]: org.xwiki.model.reference.EntityReferenceSerializer<java.lang.String> componentDependency = componentManager.getInstanceMap( ((ParameterizedType) cd.getRoleType()).getActualTypeArguments()[1]); } else { // Not a List or a Map, note that the role Type can be a ParameterizedType itself // Example: org.xwiki.model.reference.EntityReferenceSerializer<java.lang.String> componentDependency = componentManager.getInstance(cd.getRoleType(), cd.getRoleHint()); } } catch (ComponentLookupException e) { this.logger.warn(String.format( "No component found for role [%s] with hint [%s], declared as dependency for wiki component [%s]", cd.getRoleType().toString(), cd.getRoleHint(), this.wikiComponent.getDocumentReference())); } methodContext.put(dependency.getKey(), componentDependency); } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Exception { // We look for the method in the XObjects. if (!this.wikiComponent.getHandledMethods().containsKey(method.getName())) { if (method.getDeclaringClass() == Object.class || method.getDeclaringClass() == WikiComponent.class) { // return ObjectMethodsProxy.invoke(proxy, method, args); return method.invoke(wikiComponent, args); } else { // Note: We throw a runtime exception so that our exception doesn't get wrapped by a generic // UndeclaredThrowableException which would not make much sense for the user. // See http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/UndeclaredThrowableException.html throw new WikiComponentRuntimeException( String.format("You need to add an Object of type [%s] in document [%s] to implement method [%s.%s]", WikiComponentConstants.METHOD_CLASS, this.wikiComponent.getDocumentReference(), method.getDeclaringClass().getName(), method.getName())); } } else { WikiComponentMethodExecutor methodExecutor = componentManager.getInstance(WikiComponentMethodExecutor.class); Map<String, Object> methodContext = new HashMap<String, Object>(); XDOM xdom = this.wikiComponent.getHandledMethods().get(method.getName()); methodContext.put(METHOD_CONTEXT_COMPONENT_KEY, proxy); this.injectComponentDependencies(methodContext); return methodExecutor.execute(method, args, wikiComponent.getDocumentReference(), xdom, wikiComponent.getSyntax(), methodContext); } } }