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