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 }