001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.geronimo.genesis.plugins.script; 021 022 import java.io.File; 023 import java.io.InputStream; 024 import java.net.MalformedURLException; 025 import java.net.URL; 026 import java.net.URLClassLoader; 027 import java.util.ArrayList; 028 import java.util.HashMap; 029 import java.util.Iterator; 030 import java.util.List; 031 import java.util.Map; 032 import java.util.Properties; 033 034 import groovy.lang.GroovyClassLoader; 035 import groovy.lang.GroovyObject; 036 import groovy.lang.GroovyResourceLoader; 037 import groovy.lang.GroovyRuntimeException; 038 039 import org.apache.maven.artifact.Artifact; 040 import org.apache.maven.artifact.DependencyResolutionRequiredException; 041 import org.apache.maven.artifact.repository.ArtifactRepository; 042 import org.apache.maven.plugin.MojoExecutionException; 043 import org.apache.maven.project.MavenProject; 044 045 import org.apache.geronimo.genesis.MojoSupport; 046 import org.apache.geronimo.genesis.util.ArtifactItem; 047 import org.apache.geronimo.genesis.util.ExpressionParser; 048 049 /** 050 * Executes a <a href="http://groovy.codehaus.org">Groovy</a> script. 051 * 052 * @goal groovy 053 * @configurator override 054 * @requiresDependencyResolution 055 * 056 * @version $Rev: 474105 $ $Date: 2006-11-12 16:28:53 -0800 (Sun, 12 Nov 2006) $ 057 */ 058 public class GroovyMojo 059 extends MojoSupport 060 { 061 /** 062 * The source of the script to execute. 063 * 064 * @parameter 065 * @required 066 */ 067 private CodeSource source = null; 068 069 /** 070 * Additional artifacts to add to the scripts classpath. 071 * 072 * @parameter 073 */ 074 private ArtifactItem[] classpath = null; 075 076 /** 077 * Path to search for imported scripts. 078 * 079 * @parameter expression 080 */ 081 private File[] scriptpath = null; 082 083 /** 084 * A set of default project properties, which the values will be used only if 085 * the project or system does not override. 086 * 087 * @parameter 088 */ 089 private Map defaults; 090 091 /** 092 * A set of additional project properties. 093 * 094 * @parameter 095 */ 096 private Map properties; 097 098 // 099 // TODO: Find a better name for this... and figure out how to best use it to configure a custom groovy object 100 // 101 // private DelayedConfiguration custom; 102 103 // 104 // Maven components 105 // 106 107 /** 108 * @parameter expression="${project}" 109 * @readonly 110 * @required 111 */ 112 private MavenProject project = null; 113 114 /** 115 * @parameter expression="${localRepository}" 116 * @readonly 117 * @required 118 */ 119 private ArtifactRepository artifactRepository = null; 120 121 // 122 // MojoSupport Hooks 123 // 124 125 protected MavenProject getProject() { 126 return project; 127 } 128 129 protected ArtifactRepository getArtifactRepository() { 130 return artifactRepository; 131 } 132 133 // 134 // Mojo 135 // 136 137 protected void doExecute() throws Exception { 138 boolean debug = log.isDebugEnabled(); 139 140 Class type = loadGroovyClass(source); 141 GroovyObject obj = (GroovyObject)type.newInstance(); 142 143 /* 144 if (custom != null) { 145 log.info("Applying delayed configuration: " + custom); 146 147 MetaClass meta = obj.getMetaClass(); 148 MetaMethod method = meta.pickMethod(obj, "configure", new Object[] { custom }); 149 log.info("Using configure method: " + method); 150 151 method.invoke(obj, new Object[] { custom }); 152 } 153 */ 154 155 // Expose logging 156 obj.setProperty("log", log); 157 158 // Create a delegate to allow getProperites() to be fully resolved 159 MavenProject delegate = new MavenProject(project) { 160 private Properties resolvedProperties; 161 162 public Properties getProperties() { 163 if (resolvedProperties == null) { 164 resolvedProperties = resolveProperties(project.getProperties()); 165 } 166 return resolvedProperties; 167 } 168 }; 169 170 obj.setProperty("project", delegate); 171 obj.setProperty("pom", delegate); 172 173 // Execute the script 174 if (debug) { 175 log.debug("Invoking run() on: " + obj); 176 } 177 178 try { 179 obj.invokeMethod("run", new Object[0]); 180 } 181 catch (GroovyRuntimeException e) { 182 if (log.isDebugEnabled()) { 183 // Yes, log error if debug is enabled 184 log.error("Groovy script execution failure", e); 185 } 186 187 Throwable cause = e.getCause(); 188 if (cause == null) { 189 cause = e; 190 } 191 192 throw new MojoExecutionException(cause.getMessage(), cause); 193 } 194 } 195 196 private Class loadGroovyClass(final CodeSource source) throws Exception { 197 assert source != null; 198 199 boolean debug = log.isDebugEnabled(); 200 201 // Make sure the codesource us valid first 202 source.validate(); 203 204 Class type; 205 GroovyClassLoader loader = createGroovyClassLoader(); 206 207 if (source.getBody() != null) { 208 type = loader.parseClass(source.getBody()); 209 } 210 else { 211 URL url; 212 if (source.getFile() != null) { 213 url = source.getFile().toURL(); 214 } 215 else { 216 url = source.getUrl(); 217 } 218 if (debug) { 219 log.debug("Loading source from: " + url); 220 } 221 222 String fileName = new File(url.getFile()).getName(); 223 InputStream input = url.openConnection().getInputStream(); 224 try { 225 type = loader.parseClass(input, fileName); 226 } 227 finally { 228 input.close(); 229 } 230 } 231 232 return type; 233 } 234 235 private GroovyClassLoader createGroovyClassLoader() throws Exception { 236 boolean debug = log.isDebugEnabled(); 237 238 ClassLoader parent = getClass().getClassLoader(); 239 URL[] urls = getClasspath(); 240 URLClassLoader cl = new URLClassLoader(urls, parent); 241 242 // Validate and dump the scriptpath 243 if (scriptpath != null) { 244 log.debug("Scriptpath:"); 245 for (int i=0; i < scriptpath.length; i++) { 246 if (scriptpath[i] == null) { 247 throw new MojoExecutionException("Null element found in scriptpath at index: " + i); 248 } 249 250 if (debug) { 251 log.debug(" " + scriptpath[i]); 252 } 253 } 254 } 255 256 // 257 // TODO: Investigate using GroovyScript instead of this... 258 // 259 260 GroovyClassLoader loader = new GroovyClassLoader(cl); 261 262 // Allow peer scripts to be loaded 263 loader.setResourceLoader(new GroovyResourceLoader() { 264 public URL loadGroovySource(final String classname) throws MalformedURLException { 265 return resolveGroovyScript(classname); 266 } 267 }); 268 269 return loader; 270 } 271 272 private URL resolveGroovyScript(final String classname) throws MalformedURLException { 273 assert classname != null; 274 275 if (log.isDebugEnabled()) { 276 log.debug("Resolving script for class: " + classname); 277 } 278 279 String resource = classname.replace('.', '/'); 280 if (!resource.startsWith("/")) { 281 resource = "/" + resource; 282 } 283 resource = resource + ".groovy"; 284 285 // First check the scriptpath 286 if (scriptpath != null) { 287 for (int i=0; i<scriptpath.length; i++) { 288 assert scriptpath[i] != null; 289 290 File file = new File(scriptpath[i], resource); 291 if (file.exists()) { 292 return file.toURL(); 293 } 294 } 295 } 296 297 // Then look for a resource in the classpath 298 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 299 URL url = cl.getResource(resource); 300 if (url == null) { 301 // And finally check for a class defined in a file next to the main script file 302 File script = source.getFile(); 303 if (script != null) { 304 File file = new File(script.getParentFile(), resource); 305 if (file.exists()) { 306 return file.toURL(); 307 } 308 } 309 } 310 else { 311 return url; 312 } 313 314 if (log.isDebugEnabled()) { 315 log.debug("Unable to resolve script for class: " + classname); 316 } 317 318 // Else not found 319 return null; 320 } 321 322 private URL[] getClasspath() throws DependencyResolutionRequiredException, MalformedURLException, MojoExecutionException { 323 List list = new ArrayList(); 324 325 // Add the plugins dependencies 326 List classpathFiles = project.getCompileClasspathElements(); 327 for (int i = 0; i < classpathFiles.size(); ++i) { 328 list.add(new File((String)classpathFiles.get(i)).toURL()); 329 } 330 331 // Add custom dependencies 332 if (classpath != null) { 333 for (int i=0; i < classpath.length; i++) { 334 Artifact artifact = getArtifact(classpath[i]); 335 list.add(artifact.getFile().toURL()); 336 } 337 } 338 339 URL[] urls = (URL[])list.toArray(new URL[list.size()]); 340 341 // Dump the classpath 342 if (log.isDebugEnabled()) { 343 log.debug("Classpath:"); 344 for (int i=0; i < urls.length; i++) { 345 log.debug(" " + urls[i]); 346 } 347 } 348 349 return urls; 350 } 351 352 private Properties resolveProperties(final Properties source) { 353 assert source != null; 354 355 // 356 // NOTE: Create a chain of defaults 357 // 358 359 Properties dprops = new Properties(); 360 if (defaults != null) { 361 dprops.putAll(defaults); 362 } 363 364 Properties sprops = new Properties(dprops); 365 sprops.putAll(System.getProperties()); 366 367 Properties props = new Properties(sprops); 368 369 // Put all of the additional project props, which should already be resolved by mvn 370 if (properties != null) { 371 props.putAll(properties); 372 } 373 374 // Setup the variables which should be used for resolution 375 Map vars = new HashMap(); 376 vars.put("project", project); 377 378 // Resolve all source properties 379 ExpressionParser parser = new ExpressionParser(vars); 380 Iterator iter = source.keySet().iterator(); 381 while (iter.hasNext()) { 382 String name = (String)iter.next(); 383 props.put(name, parser.parse(source.getProperty(name))); 384 } 385 386 return props; 387 } 388 }