1 package org.scala_tools.maven;
2
3 import java.io.BufferedReader;
4 import java.io.File;
5 import java.io.FileOutputStream;
6 import java.io.FileReader;
7 import java.io.IOException;
8 import java.io.PrintStream;
9 import java.io.StringReader;
10 import java.lang.reflect.Constructor;
11 import java.lang.reflect.InvocationTargetException;
12 import java.net.MalformedURLException;
13 import java.net.URL;
14 import java.net.URLClassLoader;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21
22 import org.apache.maven.artifact.DependencyResolutionRequiredException;
23 import org.apache.maven.artifact.versioning.VersionRange;
24 import org.apache.maven.model.Dependency;
25 import org.apache.maven.plugin.MojoFailureException;
26 import org.apache.maven.plugin.logging.Log;
27 import org.scala_tools.maven.model.MavenProjectAdapter;
28
29
30
31
32
33
34
35
36
37
38 public class ScalaScriptMojo extends ScalaMojoSupport {
39
40
41
42
43
44
45
46
47
48
49
50 protected File outputDir;
51
52
53
54
55
56
57
58 protected File scriptFile;
59
60
61
62
63
64
65
66 protected String script;
67
68
69
70
71
72
73
74
75
76
77 protected boolean keepGeneratedScript;
78
79
80
81
82
83
84
85
86 protected String includeScopes;
87
88
89
90
91
92
93
94 protected String excludeScopes;
95
96
97
98
99
100
101 protected String addToClasspath;
102
103
104
105
106
107
108
109
110
111 protected String removeFromClasspath;
112
113 private static int currentScriptIndex = 0;
114
115 @Override
116 protected void doExecute() throws Exception {
117 if (script == null && scriptFile == null) {
118 throw new MojoFailureException(
119 "Either script or scriptFile must be defined");
120 }
121 if (script != null && scriptFile != null) {
122 throw new MojoFailureException(
123 "Only one of script or scriptFile can be defined");
124 }
125 currentScriptIndex++;
126
127
128 File scriptDir = new File(outputDir, ".scalaScriptGen");
129 scriptDir.mkdirs();
130 File destFile = new File(scriptDir + "/" + scriptBaseName() + ".scala");
131
132 Set<String> classpath = new HashSet<String>();
133 configureClasspath(classpath);
134
135 URLClassLoader loader = createScriptClassloader(scriptDir, classpath);
136
137 boolean mavenProjectDependency = hasMavenProjectDependency(classpath);
138 wrapScript(destFile, mavenProjectDependency);
139
140 try {
141 compileScript(scriptDir, destFile, classpath);
142 runScript(mavenProjectDependency, loader);
143 } finally {
144 if (!keepGeneratedScript) {
145 delete(scriptDir);
146 }
147 }
148
149 }
150
151 private boolean hasMavenProjectDependency(Set<String> classpath)
152 throws MalformedURLException {
153 try {
154 List<URL> urls = new ArrayList<URL>();
155
156
157 for (String string : classpath) {
158 urls.add(new URL("file://" + string));
159 }
160
161 URLClassLoader loader = new URLClassLoader(urls
162 .toArray(new URL[urls.size()]));
163
164 loader.loadClass(MavenProjectAdapter.class.getCanonicalName());
165 return true;
166 } catch (ClassNotFoundException e) {
167 return false;
168 }
169 }
170
171 private void runScript(boolean mavenProjectDependency, URLClassLoader loader)
172 throws Exception {
173 Class<?> compiledScript = loader.loadClass(scriptBaseName());
174
175 try {
176 try {
177 Object instance;
178 if (mavenProjectDependency) {
179 Constructor<?> constructor = compiledScript
180 .getConstructor(MavenProjectAdapter.class, Log.class);
181 instance = constructor.newInstance(new MavenProjectAdapter(
182 project), getLog());
183 } else {
184 instance = compiledScript.newInstance();
185 }
186 try {
187 compiledScript.getMethod("run").invoke(instance);
188 } catch (NoSuchMethodException e) {
189
190
191
192
193
194 }
195 } catch (InvocationTargetException e) {
196 if (e.getTargetException() != null) {
197 throw e.getTargetException();
198 } else if (e.getCause() != null) {
199 throw e.getCause();
200 } else {
201 throw e;
202 }
203 } catch (ExceptionInInitializerError e) {
204 if (e.getException() != null) {
205 throw e.getException();
206 } else if (e.getCause() != null) {
207 throw e.getCause();
208 } else {
209 throw e;
210 }
211 }
212 } catch (Throwable e) {
213 if (e instanceof Exception) {
214 throw (Exception) e;
215 } else {
216 throw new Exception("A " + e.getClass().getSimpleName()
217 + " exception was thrown", e);
218 }
219 }
220 }
221
222 private URLClassLoader createScriptClassloader(File scriptDir,
223 Set<String> classpath) throws MalformedURLException {
224 List<URL> urls = new ArrayList<URL>();
225
226
227 urls.add(scriptDir.toURI().toURL());
228
229 for (String string : classpath) {
230 urls.add(new URL("file://" + string));
231 }
232
233 URLClassLoader loader = new URLClassLoader(urls.toArray(new URL[urls
234 .size()]), getClass().getClassLoader());
235 return loader;
236 }
237
238 private void compileScript(File scriptDir, File destFile,
239 Set<String> classpath) throws Exception {
240 JavaCommand jcmd = getScalaCommand();
241 jcmd.addArgs("-classpath", JavaCommand
242 .toMultiPath(new ArrayList<String>(classpath)));
243 jcmd.addArgs("-d", scriptDir.getAbsolutePath());
244 jcmd.addArgs("-sourcepath", scriptDir.getAbsolutePath());
245 jcmd.addArgs(destFile.getAbsolutePath());
246
247 jcmd.run(displayCmd);
248 }
249
250 private void configureClasspath(Set<String> classpath) throws Exception,
251 DependencyResolutionRequiredException {
252 MavenProjectAdapter projectAdapter = new MavenProjectAdapter(project);
253
254 Collection<Dependency> toInclude = new ArrayList<Dependency>();
255 if (includeScopes == null || includeScopes.length() == 0) {
256 getLog().warn("No scopes were included");
257 } else {
258
259 String[] include = includeScopes.split(",");
260 for (String string : include) {
261 Scopes scope = Scopes.lookup(string.toUpperCase());
262 if (scope != null) {
263 toInclude.addAll(scope.elements(projectAdapter));
264 } else {
265 getLog().warn(
266 "Included Scope: " + string + " is not one of: "
267 + Arrays.asList(Scopes.values()));
268 }
269 }
270 }
271 if (excludeScopes != null && excludeScopes.length() > 0) {
272
273 String[] exclude = excludeScopes.split(",");
274 for (String string : exclude) {
275 Scopes scope = Scopes.lookup(string.toUpperCase());
276 if (scope != null) {
277 toInclude.removeAll(scope.elements(projectAdapter));
278 } else {
279 getLog().warn(
280 "Excluded Scope: " + string + " is not one of: "
281 + Arrays.asList(Scopes.values()));
282 }
283 }
284 }
285
286 for (Dependency dependency : toInclude) {
287 addToClasspath(factory.createDependencyArtifact(dependency
288 .getGroupId(), dependency.getArtifactId(), VersionRange
289 .createFromVersion(dependency.getVersion()), dependency
290 .getType(), dependency.getClassifier(), dependency
291 .getScope(), dependency.isOptional()), classpath);
292 }
293
294
295
296 if (addToClasspath != null) {
297 classpath.addAll(Arrays.asList(addToClasspath.split(",")));
298 }
299
300 if (removeFromClasspath != null) {
301 ArrayList<String> toRemove = new ArrayList<String>();
302 String[] jars = removeFromClasspath.trim().split(",");
303 for (String string : classpath) {
304 for (String jar : jars) {
305 if (string.contains(jar.trim())) {
306 toRemove.add(string);
307 }
308 }
309 }
310 classpath.removeAll(toRemove);
311 }
312
313 String outputDirectory = project.getBuild().getOutputDirectory();
314 if(!outputDirectory.endsWith("/")){
315
316 outputDirectory+="/";
317 }
318 classpath.add( outputDirectory);
319 addToClasspath("org.scala-lang", "scala-compiler", scalaVersion,
320 classpath);
321 addToClasspath("org.scala-lang", "scala-library", scalaVersion,
322 classpath);
323
324 getLog().debug("Using the following classpath for running and compiling scripts: "+classpath);
325
326 }
327
328 private void wrapScript(File destFile, boolean mavenProjectDependency)
329 throws IOException {
330 destFile.delete();
331
332 FileOutputStream fileOutputStream = new FileOutputStream(destFile);
333 PrintStream out = new PrintStream(fileOutputStream);
334 try {
335 BufferedReader reader;
336 if (scriptFile != null) {
337 reader = new BufferedReader(new FileReader(scriptFile));
338 } else {
339 reader = new BufferedReader(new StringReader(script));
340 }
341
342 if (mavenProjectDependency) {
343 out.println("import scala.collection.jcl.Conversions._");
344 out.println("class " + scriptBaseName() + "(project:"
345 + MavenProjectAdapter.class.getCanonicalName() + ",log:"+Log.class.getCanonicalName()+") {");
346 } else {
347 out.println("class " + scriptBaseName() + " {");
348 }
349
350 String line = reader.readLine();
351 while (line != null) {
352 out.print(" ");
353 out.println(line);
354 line = reader.readLine();
355 }
356
357 out.println("}");
358 } finally {
359 out.close();
360 fileOutputStream.close();
361 }
362 }
363
364 private String scriptBaseName() {
365 if (scriptFile == null) {
366 return "embeddedScript_" + currentScriptIndex;
367 } else {
368 int dot = scriptFile.getName().lastIndexOf('.');
369 if (dot == -1) {
370 return scriptFile.getName() + "_" + currentScriptIndex;
371 }
372 return scriptFile.getName().substring(0, dot) + "_"
373 + currentScriptIndex;
374 }
375 }
376
377 private void delete(File scriptDir) {
378 if (scriptDir.isDirectory()) {
379 for (File file : scriptDir.listFiles()) {
380 delete(file);
381 }
382 }
383
384 scriptDir.deleteOnExit();
385 scriptDir.delete();
386 }
387
388 private enum Scopes {
389 COMPILE {
390 public Collection<Dependency> elements(MavenProjectAdapter project)
391 throws DependencyResolutionRequiredException {
392 return project.getCompileDependencies();
393 }
394 },
395 RUNTIME {
396 public Collection<Dependency> elements(MavenProjectAdapter project)
397 throws DependencyResolutionRequiredException {
398 return project.getRuntimeDependencies();
399 }
400 },
401 TEST {
402 public Collection<Dependency> elements(MavenProjectAdapter project)
403 throws DependencyResolutionRequiredException {
404 return project.getTestDependencies();
405 }
406 },
407 SYSTEM {
408 public Collection<Dependency> elements(MavenProjectAdapter project)
409 throws DependencyResolutionRequiredException {
410 return project.getSystemDependencies();
411 }
412 };
413
414 public abstract Collection<Dependency> elements(
415 MavenProjectAdapter project)
416 throws DependencyResolutionRequiredException;
417
418 public static Scopes lookup(String name) {
419 for (Scopes scope : Scopes.values()) {
420 if (scope.name().trim().equalsIgnoreCase(name.trim())) {
421 return scope;
422 }
423 }
424 return null;
425 }
426 }
427 }