View Javadoc

1   /*
2    * $HeadURL: https://mindtreeinsight.svn.sourceforge.net/svnroot/mindtreeinsight/releng/maven-jsmooth-plugin/trunk/src/main/java/com/mindtree/techworks/insight/releng/mvn/jsmooth/JSmoothCompileMojo.java $
3    * 
4    * Copyright (c) 2007 MindTree Consulting Ltd. 
5    * 
6    * This file is part of Insight  Release Engineering Tools.
7    * 
8    * Insight  Release Engineering Tools is free software: you can redistribute it 
9    * and/or modify it under the terms of the GNU General Public License as 
10   * published by the Free Software Foundation, either version 3 of the License, 
11   * or (at your option) any later version.
12   * 
13   * Insight  Release Engineering Tools is distributed in the hope that it will be 
14   * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
16   * Public License for more details.
17   * 
18   * You should have received a copy of the GNU General Public License along with 
19   * Insight  Release Engineering Tools. If not, see <http://www.gnu.org/licenses/>.
20   */
21  package com.mindtree.techworks.insight.releng.mvn.jsmooth;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.net.URISyntaxException;
26  import java.net.URL;
27  import java.security.CodeSource;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Set;
33  import java.util.zip.ZipFile;
34  
35  import net.charabia.jsmoothgen.application.ExeCompiler;
36  import net.charabia.jsmoothgen.application.JSmoothModelBean;
37  import net.charabia.jsmoothgen.application.JSmoothModelPersistency;
38  import net.charabia.jsmoothgen.skeleton.SkeletonBean;
39  import net.charabia.jsmoothgen.skeleton.SkeletonList;
40  
41  import org.apache.maven.artifact.Artifact;
42  import org.apache.maven.plugin.AbstractMojo;
43  import org.apache.maven.plugin.MojoExecutionException;
44  import org.apache.maven.plugin.MojoFailureException;
45  import org.apache.maven.project.MavenProject;
46  import org.apache.maven.project.MavenProjectHelper;
47  
48  /**
49   * This is the entry class for the maven plugin to compile a Jsmooth file.
50   * 
51   * <br/> The following tasks are accomplished in order:
52   * <ol>
53   * <li>Create a temporary folder under 'target'</li>
54   * <li>Copy the jsmooth file to the target folder</li>
55   * <li>Apply any filters required to the .jsmooth file</li>
56   * <li>Call Jsmooth's {@link ExeCompiler}</li>
57   * </ol>
58   * 
59   * @see AbstractMojo
60   * 
61   * @author <a href="mailto:bindul_bhowmik@mindtree.com">Bindul Bhowmik</a>
62   * @version $Revision: 171 $ $Date: 2008-04-17 02:16:43 -0600 (Thu, 17 Apr 2008) $
63   * 
64   * @goal jsmoothcompile
65   * @requiresDependencyResolution test
66   * @requiresDependencyResolution compile
67   * @requiresDependencyResolution runtime
68   * @description Compiles the Jsmooth executable for the project
69   */
70  public class JSmoothCompileMojo extends AbstractMojo {
71  
72  	/**
73  	 * Flag to allow one or more executions of the build to skip the compile
74  	 * 
75  	 * @parameter default-value="false"
76  	 */
77  	private boolean skipJSmooth;
78  
79  	/**
80  	 * The relative location of the jsmooth file
81  	 * 
82  	 * @parameter
83  	 * @required
84  	 */
85  	private String jsmoothFile;
86  
87  	/**
88  	 * The location of the icon file
89  	 * 
90  	 * @parameter
91  	 */
92  	private String iconLocation;
93  
94  	/**
95  	 * Flag to indicate if the exe name generated in the build process is to be
96  	 * replaced in the jsmooth file
97  	 * 
98  	 * @parameter default-value="false"
99  	 */
100 	private boolean setExeName;
101 
102 	/**
103 	 * Flag to indicate if the jar name generated in the build process is to be
104 	 * replaced in the jsmooth file
105 	 * 
106 	 * @parameter default-value="true"
107 	 */
108 	private boolean setJarName;
109 
110 	/**
111 	 * If dependencies are to be set from a base directory, the name of the base
112 	 * directory
113 	 * 
114 	 * @parameter
115 	 */
116 	private String dependencyBaseDir;
117 
118 	/**
119 	 * Flag to indicate if dependencies are to be included
120 	 * 
121 	 * @parameter default-value="true"
122 	 */
123 	private boolean includeDependencies;
124 
125 	/**
126 	 * The optional parameter to indicate the scope of dependencies to include.
127 	 * The allowed values are runtime, compile and test.
128 	 * 
129 	 * @parameter default-value="runtime"
130 	 */
131 	private String dependencyScope;
132 
133 	/**
134 	 * The maven project we are working in
135 	 * 
136 	 * @parameter default-value="${project}"
137 	 * @required
138 	 * @readonly
139 	 */
140 	private MavenProject project;
141 	
142 	/**
143      * Maven ProjectHelper.
144      *
145      * @component
146      */
147     private MavenProjectHelper projectHelper;
148 
149 	/*
150 	 * (non-Javadoc)
151 	 * 
152 	 * @see org.apache.maven.plugin.Mojo#execute()
153 	 */
154 	public void execute() throws MojoExecutionException, MojoFailureException {
155 		if (skipJSmooth) {
156 			getLog()
157 					.info(
158 							"Skipping JSmooth Compile, as skipJSmooth flag is set to false");
159 		}
160 
161 		// Resolve the Jsmooth file path
162 		File jSmthFile = resolveFile(jsmoothFile);
163 		getLog().info("Using jsmooth file... " + jSmthFile.getAbsolutePath());
164 
165 		// Load the jsmooth file
166 		JSmoothModelBean jsmoothModel = null;
167 		try {
168 			jsmoothModel = JSmoothModelPersistency.load(jSmthFile);
169 		} catch (IOException e) {
170 			getLog().error("IOException while reading jsmooth file", e);
171 			throw new MojoFailureException(this,
172 					"IOException reading jsmooth file", e.getMessage());
173 		}
174 
175 		// Modify stuff
176 		manipulateModelFile(jsmoothModel);
177 
178 		// Create the temporary directory
179 		File jsmoothTmpDir = createTemporaryDirectory();
180 
181 		// Write the modified jsmooth file for debugging
182 		writeJsmoothFile(jsmoothTmpDir, jsmoothModel);
183 
184 		// Write out the skeletons
185 		File skeletonDir = writeSkeletons(jsmoothTmpDir);
186 
187 		// Compile stuff
188 		File compiledFile = compileJSmooth(skeletonDir, jsmoothModel);
189 		
190 		projectHelper.attachArtifact(project, "exe", compiledFile);
191 	}
192 
193 	private File compileJSmooth(File skeletonRoot, JSmoothModelBean model) throws MojoExecutionException {
194 		try {
195 			SkeletonList skelList = new SkeletonList(skeletonRoot);
196 
197 			File out = new File(project.getBuild().getDirectory(), model
198 					.getExecutableName());
199 			SkeletonBean skel = skelList.getSkeleton(model.getSkeletonName());
200 			File skelroot = skelList.getDirectory(skel);
201 
202 			final ExeCompiler compiler = new ExeCompiler();
203 			compiler.addListener(new ExeCompiler.StepListener() {
204 
205 				public void complete() {
206 					getLog().info("JSmooth generation complete");
207 				}
208 
209 				public void failed() {
210 					getLog().error("JSmooth generation failed");
211 				}
212 
213 				public void setNewState(int percentComplete, String state) {
214 
215 					getLog().debug(
216 							"jsmooth: " + state + " ( " + percentComplete
217 									+ "%)");
218 				}
219 			});
220 
221 			if (compiler.compile(skelroot, skel, new File(project.getBuild()
222 					.getDirectory()), model, out)) {
223 				getLog().info(
224 						"Java application wrapped in "
225 								+ model.getExecutableName());
226 			} else {
227 				getLog().error("jsmoothgen failed: " + compiler.getErrors());
228 				throw new MojoExecutionException(this, "jsmoothgen failed: ", compiler.getErrors().toString());
229 			}
230 			
231 			return out;
232 		} catch (MojoExecutionException exc) {
233 			throw exc;
234 		} catch (Exception exc) {
235 			throw new MojoExecutionException(this, "Error building the jsmooth wrapper", exc.getMessage());
236 		}
237 
238 	}
239 
240 	/**
241 	 * Writes the jsmooth skeletons to the specified directory
242 	 * 
243 	 * @param jsmoothTmpDir
244 	 *            The temporary directory under which to create the skeletons
245 	 *            directory
246 	 * @return The file reference to the skeleton directory
247 	 * @throws MojoFailureException
248 	 *             If there is a failure is extracting the skeletons
249 	 * @throws MojoExecutionException
250 	 *             Error copying skeleton directory
251 	 */
252 	private File writeSkeletons(File jsmoothTmpDir)
253 			throws MojoFailureException, MojoExecutionException {
254 		// Get the code source and get to the jar file
255 		CodeSource cs = getClass().getProtectionDomain().getCodeSource();
256 		if (null == cs) {
257 			// Can't proceed
258 			throw new MojoFailureException(
259 					"Unable to determine the source of this jar and hence cannot extract skeletons");
260 		}
261 		URL baseUrl = cs.getLocation();
262 		getLog().debug("Determined code source location: " + baseUrl.toExternalForm());
263 		File destinationDir = new File(jsmoothTmpDir + File.separator
264 				+ "skeletons");
265 		try {
266 			// Determine the File object. This has been modified from the 
267 			// previous implementation to handle illegal characters in URLs.
268 			File basePath = null;
269 			try {
270 				basePath = new File(baseUrl.toURI());
271 			} catch (URISyntaxException e) {
272 				getLog().debug("Failed the URI method.. faling back to path");
273 				basePath = new File(baseUrl.getPath());
274 			}
275 			getLog().debug(
276 					"Attempting extraction from" + basePath.getAbsolutePath());
277 
278 			// Check extraction from zip
279 			if (basePath.getAbsolutePath().endsWith(".jar")
280 					|| basePath.getAbsolutePath().endsWith(".zip")) {
281 				ZipFile sourceFile = new ZipFile(basePath);
282 				FileUtils.extractZipDirectories(sourceFile, destinationDir,
283 						"skeletons", true, getLog());
284 			} else {
285 				File skeletonDir = new File(basePath.getAbsolutePath()
286 						+ File.separator + "skeletons");
287 				if (skeletonDir.exists()) {
288 					FileUtils.copyDirectory(skeletonDir, destinationDir);
289 				} else {
290 					throw new MojoFailureException(
291 							"Unable to determine the source of this jar and hence cannot extract skeletons");
292 				}
293 			}
294 		} catch (IOException e) {
295 			// Error copying file
296 			throw new MojoExecutionException(this,
297 					"Error copying skeleton files", e.getMessage());
298 		}
299 		return destinationDir;
300 	}
301 
302 	/**
303 	 * Writes the jsmooth file to the specified directory
304 	 * 
305 	 * @param jsmoothTmpDir
306 	 *            The directory to which to write to
307 	 * @param jsmoothModel
308 	 *            The model to write
309 	 */
310 	private void writeJsmoothFile(File jsmoothTmpDir,
311 			JSmoothModelBean jsmoothModel) {
312 		File tmpFileNm = new File(jsmoothFile);
313 		String tempJSmoothFileName = jsmoothTmpDir.getAbsolutePath()
314 				+ File.separator + tmpFileNm.getName();
315 		try {
316 			JSmoothModelPersistency.save(new File(tempJSmoothFileName),
317 					jsmoothModel);
318 		} catch (IOException e) {
319 			getLog().warn("IOException writing jsmooth file", e);
320 			// This is not a fatal error
321 		}
322 	}
323 
324 	/**
325 	 * Creates the temporary directory for use
326 	 * 
327 	 * @return The file reference to the temporary directory
328 	 * @throws MojoExecutionException
329 	 *             If the temporary directory cannot be written
330 	 */
331 	private File createTemporaryDirectory() throws MojoExecutionException {
332 		String jsmoothTmpDirPath = project.getBuild().getDirectory()
333 				+ File.separator + "jsmooth-temp";
334 		File jsmoothTmpDir = new File(jsmoothTmpDirPath);
335 		if (!jsmoothTmpDir.exists()) {
336 			if (!jsmoothTmpDir.mkdir()) {
337 				throw new MojoExecutionException(
338 						"Cannot create temporary directory");
339 			}
340 		}
341 
342 		return jsmoothTmpDir;
343 	}
344 
345 	/**
346 	 * Modifies the parametes of the jsmooth model file
347 	 * 
348 	 * @param jsmoothModel
349 	 *            The jsmooth model
350 	 * @throws MojoExecutionException
351 	 *             If the required file cannot be used.
352 	 */
353 	private void manipulateModelFile(JSmoothModelBean jsmoothModel)
354 			throws MojoExecutionException {
355 
356 		// Set the icon location
357 		if (null != iconLocation && iconLocation.trim().length() > 0) {
358 			File iconFileLoc = resolveFile(iconLocation);
359 			getLog().info(
360 					"Setting Icon location to : "
361 							+ iconFileLoc.getAbsolutePath());
362 			jsmoothModel.setIconLocation(iconFileLoc.getAbsolutePath());
363 		}
364 
365 		String finalBuildName = project.getBuild().getFinalName();
366 
367 		if (setExeName) {
368 			getLog().info(
369 					"Setting final output name to :" + finalBuildName + ".exe");
370 			jsmoothModel.setExecutableName(finalBuildName + ".exe");
371 		}
372 
373 		if (setJarName) {
374 			// Set the jar file name
375 			getLog().info("Setting jar name to :" + finalBuildName + ".jar");
376 			jsmoothModel.setJarLocation(finalBuildName + ".jar");
377 		}
378 
379 		if (includeDependencies) {
380 			getLog().debug(
381 					"Including dependencies with base scope: "
382 							+ dependencyScope);
383 			List impliedScopes = getImpliedScopes(dependencyScope);
384 			Set dependencyArtifacts = project.getArtifacts();
385 			ArrayList dependentArtifacts = new ArrayList(dependencyArtifacts
386 					.size());
387 
388 			String baseDir = "";
389 			if (null != dependencyBaseDir) {
390 				baseDir = dependencyBaseDir.trim();
391 				if (!baseDir.endsWith("/") || !baseDir.endsWith(File.separator)) {
392 					baseDir = baseDir + File.separator;
393 				}
394 			}
395 
396 			for (Iterator dependencyItr = dependencyArtifacts.iterator(); dependencyItr
397 					.hasNext();) {
398 				Artifact dependencyArtifact = (Artifact) dependencyItr.next();
399 				if (impliedScopes.contains(dependencyArtifact.getScope())) {
400 					getLog().debug(
401 							"Including dependency: "
402 									+ dependencyArtifact.getId());
403 					String dependencyPath = baseDir
404 							+ dependencyArtifact.getFile().getName();
405 					getLog().debug("Inluding dependency as: " + dependencyPath);
406 					dependentArtifacts.add(dependencyPath);
407 				}
408 			}
409 
410 			if (!dependentArtifacts.isEmpty()) {
411 				String[] existingClasspath = jsmoothModel.getClassPath();
412 				if (null != existingClasspath && existingClasspath.length > 0) {
413 					Collections.addAll(dependentArtifacts, existingClasspath);
414 				}
415 
416 				jsmoothModel.setClassPath((String[]) dependentArtifacts
417 						.toArray(new String[dependentArtifacts.size()]));
418 			}
419 		}
420 	}
421 
422 	/**
423 	 * Gets the list of implied scopes for a a given scope. The scope tree used
424 	 * for this resolution is:
425 	 * <ol>
426 	 * <li>compile</li>
427 	 * <li>runtime</li>
428 	 * <li>test</li>
429 	 * </ol>
430 	 * 
431 	 * @param scope
432 	 * @return
433 	 */
434 	private List getImpliedScopes(String scope) {
435 		List resolvedScopeList = new ArrayList();
436 
437 		if (Artifact.SCOPE_COMPILE.equals(scope)) {
438 			resolvedScopeList.add(Artifact.SCOPE_COMPILE);
439 		} else if (Artifact.SCOPE_RUNTIME.equals(scope)) {
440 			resolvedScopeList.add(Artifact.SCOPE_COMPILE);
441 			resolvedScopeList.add(Artifact.SCOPE_RUNTIME);
442 		} else if (Artifact.SCOPE_TEST.equals(scope)) {
443 			resolvedScopeList.add(Artifact.SCOPE_COMPILE);
444 			resolvedScopeList.add(Artifact.SCOPE_RUNTIME);
445 			resolvedScopeList.add(Artifact.SCOPE_TEST);
446 		}
447 
448 		return resolvedScopeList;
449 	}
450 
451 	/**
452 	 * Gets the Jsmooth file being used. This method assumes the file indicated
453 	 * by jsmoothfile is an absolute file, and then tries to resolve it as a
454 	 * relative path to the project base directory.
455 	 * 
456 	 * @return The file location of the jsmooth file
457 	 * @throws MojoExecutionException
458 	 *             If the file is not found.
459 	 */
460 	private File resolveFile(String path) throws MojoExecutionException {
461 
462 		getLog().debug("Resolving path: " + path);
463 
464 		// Check for empty value
465 		if (null == path || path.trim().length() == 0) {
466 			throw new MojoExecutionException("The path location is required.");
467 		}
468 
469 		// Assume absolute file location
470 		File fileLoc = new File(path);
471 		if (fileLoc.exists() && fileLoc.canRead() && fileLoc.isFile()) {
472 			return fileLoc;
473 		}
474 
475 		// If we are here, then the file must be relative
476 		File baseDir = project.getBasedir();
477 		getLog().debug(
478 				"Using base directory path : " + baseDir.getAbsolutePath());
479 		String finalFilePath = baseDir.getAbsolutePath() + File.separatorChar
480 				+ path;
481 		getLog().debug("Computed file path : " + finalFilePath);
482 		fileLoc = new File(finalFilePath);
483 		if (fileLoc.exists() && fileLoc.canRead() && fileLoc.isFile()) {
484 			return fileLoc;
485 		}
486 
487 		// If we are here, then the file does not exist at all!
488 		throw new MojoExecutionException("The file specified [" + path
489 				+ "] either does not exist, or cannot be resolved "
490 				+ "or cannot be read.");
491 	}
492 
493 	/**
494 	 * @return the skipJSmooth
495 	 */
496 	public boolean isSkipJSmooth() {
497 		return skipJSmooth;
498 	}
499 
500 	/**
501 	 * @param skipJSmooth
502 	 *            the skipJSmooth to set
503 	 */
504 	public void setSkipJSmooth(boolean skipJSmooth) {
505 		this.skipJSmooth = skipJSmooth;
506 	}
507 
508 	/**
509 	 * @return the jsmoothFile
510 	 */
511 	public String getJsmoothFile() {
512 		return jsmoothFile;
513 	}
514 
515 	/**
516 	 * @param jsmoothFile
517 	 *            the jsmoothFile to set
518 	 */
519 	public void setJsmoothFile(String jsmoothFile) {
520 		this.jsmoothFile = jsmoothFile;
521 	}
522 
523 	/**
524 	 * @return the project
525 	 */
526 	public MavenProject getProject() {
527 		return project;
528 	}
529 
530 	/**
531 	 * @return the iconLocation
532 	 */
533 	public String getIconLocation() {
534 		return iconLocation;
535 	}
536 
537 	/**
538 	 * @param iconLocation
539 	 *            the iconLocation to set
540 	 */
541 	public void setIconLocation(String iconLocation) {
542 		this.iconLocation = iconLocation;
543 	}
544 
545 	/**
546 	 * @return the setExeName
547 	 */
548 	public boolean isSetExeName() {
549 		return setExeName;
550 	}
551 
552 	/**
553 	 * @param setExeName
554 	 *            the setExeName to set
555 	 */
556 	public void setSetExeName(boolean setExeName) {
557 		this.setExeName = setExeName;
558 	}
559 
560 	/**
561 	 * @return the setJarName
562 	 */
563 	public boolean isSetJarName() {
564 		return setJarName;
565 	}
566 
567 	/**
568 	 * @param setJarName
569 	 *            the setJarName to set
570 	 */
571 	public void setSetJarName(boolean setJarName) {
572 		this.setJarName = setJarName;
573 	}
574 
575 	/**
576 	 * @return the includeDependencies
577 	 */
578 	public boolean isIncludeDependencies() {
579 		return includeDependencies;
580 	}
581 
582 	/**
583 	 * @param includeDependencies
584 	 *            the includeDependencies to set
585 	 */
586 	public void setIncludeDependencies(boolean includeDependencies) {
587 		this.includeDependencies = includeDependencies;
588 	}
589 
590 	/**
591 	 * @return the dependencyBaseDir
592 	 */
593 	public String getDependencyBaseDir() {
594 		return dependencyBaseDir;
595 	}
596 
597 	/**
598 	 * @param dependencyBaseDir
599 	 *            the dependencyBaseDir to set
600 	 */
601 	public void setDependencyBaseDir(String dependencyBaseDir) {
602 		this.dependencyBaseDir = dependencyBaseDir;
603 	}
604 
605 	/**
606 	 * @return the dependencyScope
607 	 */
608 	public String getDependencyScope() {
609 		return dependencyScope;
610 	}
611 
612 	/**
613 	 * @param dependencyScope
614 	 *            the dependencyScope to set
615 	 */
616 	public void setDependencyScope(String dependencyScope) {
617 		this.dependencyScope = dependencyScope;
618 	}
619 
620 }