001 /* 002 * Copyright (C) 2006-2007 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.codehaus.gmaven.runtime.support; 018 019 import org.codehaus.gmaven.feature.ComponentException; 020 import org.codehaus.gmaven.feature.Configuration; 021 import org.codehaus.gmaven.feature.Feature; 022 import org.codehaus.gmaven.feature.support.ComponentSupport; 023 import org.codehaus.gmaven.runtime.ClassFactory; 024 import org.codehaus.gmaven.runtime.ScriptExecutor; 025 import org.codehaus.gmaven.runtime.util.Callable; 026 import org.codehaus.gmaven.runtime.util.ClassSource; 027 import org.codehaus.gmaven.runtime.util.MagicAttribute; 028 import org.codehaus.gmaven.runtime.util.ResourceLoader; 029 030 import java.lang.reflect.InvocationTargetException; 031 import java.lang.reflect.Method; 032 import java.util.Arrays; 033 import java.util.Iterator; 034 035 /** 036 * Support for {@link ScriptExecutor} component implementations. 037 * 038 * @version $Id: ScriptExecutorSupport.java 83 2009-12-11 11:01:28Z user57 $ 039 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> 040 */ 041 public abstract class ScriptExecutorSupport 042 extends ComponentSupport 043 implements ScriptExecutor 044 { 045 protected ScriptExecutorSupport(final Feature feature, final Configuration config) { 046 super(feature, config); 047 } 048 049 protected ScriptExecutorSupport(final Feature feature) { 050 super(feature); 051 } 052 053 protected abstract ClassFactory getClassFactory(); 054 055 public Object execute(final ClassSource source, final ClassLoader classLoader, final ResourceLoader resourceLoader, final Configuration context) throws Exception { 056 assert source != null; 057 assert classLoader != null; 058 // resourceLoader may be null 059 // context may be null 060 061 // Create/load the class 062 Class type = getClassFactory().create(source, classLoader, resourceLoader); 063 064 // Create a new instance 065 Object target = type.newInstance(); 066 067 // Apply the execution context if we have one 068 if (context != null) { 069 applyContext(target, context); 070 } 071 072 // And then execute 073 return execute(target); 074 } 075 076 protected abstract Object createClosure(Callable target); 077 078 protected abstract Object createMagicAttribute(MagicAttribute attr); 079 080 // 081 // NOTE: Using reflection here to invoke setProperty() and run() to avoid evil class loading problems. 082 // 083 084 protected void applyContext(final Object target, final Configuration context) { 085 assert target != null; 086 assert context != null; 087 088 Method setter = lookupMethod(target.getClass(), "setProperty", new Class[] { String.class, Object.class }); 089 090 log.debug("Setting context:"); 091 092 // Install the context for the script 093 for (Iterator iter = context.names().iterator(); iter.hasNext();) { 094 String name = (String) iter.next(); 095 Object value = context.get(name); 096 097 // Adapt Callable instances into Closures 098 if (value instanceof Callable) { 099 Callable c = (Callable) value; 100 101 value = createClosure(c); 102 } 103 104 // Handle some uber hacks 105 else if (value instanceof MagicAttribute) { 106 MagicAttribute attr = (MagicAttribute) value; 107 108 value = createMagicAttribute(attr); 109 } 110 111 if (log.isDebugEnabled()) { 112 if (value != null) { 113 log.debug(" {} -> {} ({})", new Object[] { name, value, value.getClass() }); 114 } 115 else { 116 log.debug(" {} -> {}", name, value); 117 } 118 } 119 120 invokeMethod(target, setter, new Object[] { name, value }); 121 } 122 } 123 124 protected Object execute(final Object target) { 125 assert target != null; 126 127 Method run = lookupMethod(target.getClass(), "run", new Class[0]); 128 129 // 130 // TODO: Look at Groovysh to allow for more flexible script execution 131 // 132 133 return invokeMethod(target, run, new Object[0]); 134 } 135 136 // 137 // Reflection Helpers 138 // 139 140 protected Method lookupMethod(final Class type, final String name, final Class[] sig) { 141 assert type != null; 142 assert name != null; 143 assert sig != null; 144 145 Method method; 146 147 try { 148 method = type.getMethod(name, sig); 149 } 150 catch (Exception e) { 151 throw new ComponentException("Failed to lookup method '" + name + "()' on: " + type, e); 152 } 153 154 return method; 155 } 156 157 protected Object invokeMethod(final Object target, final Method method, final Object[] args) { 158 assert target != null; 159 assert method != null; 160 assert args != null; 161 162 if (log.isTraceEnabled()) { 163 log.trace("Invoking {} on {} with {}", new Object[] {method, target, Arrays.asList(args)}); 164 } 165 166 try { 167 return method.invoke(target, args); 168 } 169 catch (InvocationTargetException e) { 170 // Unroll ITE to reduce the trace noise 171 throw new ComponentException(e.getTargetException()); 172 } 173 catch (Exception e) { 174 throw new ComponentException("Failed to invoke method '" + method + "' on target: " + target, e); 175 } 176 } 177 }