View Javadoc
1   package org.andromda.maven.plugin.andromdapp;
2   
3   import java.io.File;
4   import java.io.FileReader;
5   import java.net.URL;
6   import java.util.ArrayList;
7   import java.util.LinkedHashMap;
8   import java.util.List;
9   import java.util.Map;
10  import org.apache.commons.io.FileUtils;
11  import org.apache.maven.artifact.Artifact;
12  import org.apache.maven.artifact.factory.ArtifactFactory;
13  import org.apache.maven.model.Model;
14  import org.apache.maven.model.Parent;
15  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
16  import org.apache.maven.plugin.AbstractMojo;
17  import org.apache.maven.plugin.MojoExecutionException;
18  import org.apache.maven.plugin.MojoFailureException;
19  import org.apache.maven.project.MavenProject;
20  import org.codehaus.plexus.util.DirectoryScanner;
21  
22  /**
23   * Basically post processes a previously built ear and replaces any war
24   * artifacts with symbolic links and then symbolic links the ear to the deploy
25   * directory so that we don't have to redeploy an ear in order to make jsp changes.
26   *
27   * @author Chad Brandon
28   * @goal link
29   * @phase package
30   */
31  public class SymbolicLinkExplodedEarMojo
32      extends AbstractMojo
33  {
34      /**
35       * The maven project.
36       *
37       * @parameter expression="${project}"
38       * @required
39       * @readonly
40       */
41      protected MavenProject project;
42  
43      /**
44       * The location in which to link the exploded ear.
45       *
46       * @parameter expression="${env.JBOSS_HOME}/server/default/deploy"
47       */
48      private String deployLocation;
49  
50      /**
51       * The number of levels allowed to travel up before we get to the "root project" (i.e. this
52       * will prevent the system from attempting to get parent project that aren't really
53       * part of the direct project).
54       *
55       * @parameter expression="1"
56       */
57      private int rootProjectLimit;
58  
59      /**
60       * Artifact factory, needed to download source jars for inclusion in
61       * classpath.
62       *
63       * @component role="org.apache.maven.artifact.factory.ArtifactFactory"
64       * @required
65       * @readonly
66       */
67      private ArtifactFactory artifactFactory;
68  
69      /**
70       * Command to create a symbolic link on the Windows platform
71       */
72      private static final String LN_WINDOWS = "junction.exe";
73  
74      /**
75       * Command to create a symbolic link on the UNIX platform
76       */
77      private static final String LN_UNIX = "ln -s";
78  
79      /**
80       * <code>true</code> if the base OS is Windows.
81       */
82      private static  boolean isWindows = false;
83  
84      private static final String WINDOWS = "Windows";
85  
86      static
87      {
88          final String osName = System.getProperty("os.name");
89          if (osName.contains(WINDOWS))
90          {
91              isWindows = true;
92          }
93      }
94  
95      /**
96       * @see org.apache.maven.plugin.Mojo#execute()
97       */
98      public void execute()
99          throws MojoExecutionException, MojoFailureException
100     {
101         try
102         {
103             final File earDirectory =
104                 new File(this.project.getBuild().getDirectory() + '/' + project.getBuild().getFinalName());
105             final Map<String, Artifact> artifacts = new LinkedHashMap<String, Artifact>();
106             for (final Artifact artifact : this.getExplodedModuleArtifacts())
107             {
108                 artifacts.put(
109                     artifact.getFile().getName(),
110                     artifact);
111             }
112             if (earDirectory.exists() && earDirectory.isDirectory())
113             {
114                 String linkCommand;
115                 if (isWindows)
116                 {
117                     linkCommand = LN_WINDOWS;
118                     final File executableFile =
119                         new File(System.getProperty("java.io.tmpdir") + '/' + System.getProperty("user.name"),
120                             LN_WINDOWS);
121                     if (!executableFile.exists())
122                     {
123                         final URL resource =
124                             Thread.currentThread().getContextClassLoader().getResource(LN_WINDOWS);
125                         FileUtils.copyURLToFile(
126                             resource,
127                             executableFile);
128                     }
129                     linkCommand = executableFile.toString();
130                 }
131                 else
132                 {
133                     linkCommand = LN_UNIX;
134                 }
135                 final File[] files = earDirectory.listFiles();
136                 final File explodedEarDirectory = new File(earDirectory + "-exploded");
137                 explodedEarDirectory.mkdirs();
138                 for (int ctr = 0; ctr < files.length; ctr++)
139                 {
140                     final File file = files[ctr];
141                     final String fileName = file.getName();
142                     final Artifact artifact = artifacts.get(fileName);
143                     if (artifact == null)
144                     {
145                         if (file.isFile())
146                         {
147                             FileUtils.copyFileToDirectory(
148                                 file,
149                                 explodedEarDirectory);
150                         }
151                         else
152                         {
153                             final File targetDirectory = new File(explodedEarDirectory,
154                                     file.getName());
155                             targetDirectory.mkdir();
156                             FileUtils.copyDirectory(
157                                 file,
158                                 targetDirectory);
159                         }
160                     }
161                     else
162                     {
163                         final File targetFile = new File(explodedEarDirectory, fileName);
164                         final File explodedArtifactDirectory =
165                             new File(artifact.getFile().toString().replaceAll(
166                                     "\\." + artifact.getType(),
167                                     ""));
168                         final String command;
169                         if (!isWindows)
170                         {
171                             command = linkCommand + ' ' + explodedArtifactDirectory + ' ' +
172                                 targetFile.getAbsolutePath();
173                         }
174                         else
175                         {
176                             command = linkCommand + ' ' + targetFile.getAbsolutePath() + ' ' +
177                                 explodedArtifactDirectory;
178                         }
179                         if (this.getLog().isDebugEnabled())
180                         {
181                             this.getLog().debug("executing command: " + command);
182                         }
183                         // - remove the archive artifact if its already present
184                         if (targetFile.exists() && targetFile.isFile())
185                         {
186                             targetFile.delete();
187                         }
188                         if (targetFile.isDirectory())
189                         {
190                             this.getLog().info("NOT linking " + explodedArtifactDirectory + " to " + targetFile + " (already linked)");
191                         }
192                         else
193                         {
194                             this.getLog().info("linking " + explodedArtifactDirectory + " to " + targetFile);
195                             Runtime.getRuntime().exec(command);
196                         }
197                     }
198                 }
199 
200                 final File targetFile =
201                     new File(this.deployLocation,
202                         this.project.getBuild().getFinalName() + '.' + this.project.getPackaging());
203                 final String command;
204                 if (!isWindows)
205                 {
206                     command = linkCommand + ' ' + explodedEarDirectory + ' ' + targetFile.getAbsolutePath();
207                 }
208                 else
209                 {
210                     command = linkCommand + ' ' + targetFile.getAbsolutePath() + ' ' + explodedEarDirectory;
211                 }
212                 if (this.getLog().isDebugEnabled())
213                 {
214                     this.getLog().debug("executing command: " + command);
215                 }
216                 final File applicationContextXml = new File(explodedEarDirectory, "META-INF/application.xml");
217                 FileUtils.touch(applicationContextXml);
218 
219                 // - remove the ear file if it exists
220                 if (targetFile.isFile())
221                 {
222                     targetFile.delete();
223                 }
224                 if (targetFile.isDirectory())
225                 {
226                     this.getLog().info("NOT linking " + explodedEarDirectory + " to " + targetFile + " (already linked)");
227                 }
228                 else
229                 {
230                     this.getLog().info("linking " + explodedEarDirectory + " to " + targetFile);
231                     Runtime.getRuntime().exec(command);
232                 }
233             }
234         }
235         catch (Exception exception)
236         {
237             throw new MojoExecutionException("A failure occurred while trying to link the ear", exception);
238         }
239     }
240 
241     /**
242      * Stores the root project.
243      */
244     private MavenProject rootProject;
245 
246     /**
247      * Retrieves the root project (i.e. the root parent project)
248      * for this project.
249      *
250      * @return the root project.
251      * @throws MojoExecutionException
252      */
253     private MavenProject getRootProject()
254         throws MojoExecutionException
255     {
256         if (this.rootProject == null)
257         {
258             MavenProject root = null;
259             int ctr = 1;
260             for (root = this.project.getParent(); root.getParent() != null && ctr < this.rootProjectLimit; root = root.getParent(), ctr++)
261             {
262             }
263             this.rootProject = root;
264         }
265         return this.rootProject;
266     }
267 
268     private static final String[] INCLUDE_ALL_POMS = new String[]{"*/**/pom.xml"};
269 
270     /**
271      * Retrieves all the POMs for the given project.
272      *
273      * @return all poms found.
274      * @throws MojoExecutionException
275      */
276     private List<File> getPoms()
277         throws MojoExecutionException
278     {
279         final DirectoryScanner scanner = new DirectoryScanner();
280         scanner.setBasedir(this.getRootProject().getBasedir());
281         scanner.setIncludes(INCLUDE_ALL_POMS);
282         scanner.scan();
283         final List<File> poms = new ArrayList<File>();
284         for (int ctr = 0; ctr < scanner.getIncludedFiles().length; ctr++)
285         {
286             final File file = new File(
287                     this.getRootProject().getBasedir(),
288                     scanner.getIncludedFiles()[ctr]);
289             if (file.exists())
290             {
291                 poms.add(file);
292             }
293         }
294         return poms;
295     }
296 
297     /**
298      * Constructs an artifact from the given <code>pom</code> file.
299      *
300      * @return all module artifacts
301      * @throws Exception
302      */
303     private List<Artifact> getExplodedModuleArtifacts()
304         throws Exception
305     {
306         final List<Artifact> artifacts = new ArrayList<Artifact>();
307         final MavenXpp3Reader reader = new MavenXpp3Reader();
308 
309         for (final File pom : this.getPoms())
310         {
311             FileReader freader = new FileReader(pom);
312             final Model model = reader.read(freader);
313             String groupId = model.getGroupId();
314             for (Parent parent = model.getParent(); groupId == null && model.getParent() != null;
315                 parent = model.getParent())
316             {
317                 groupId = parent.getGroupId();
318             }
319             String version = model.getVersion();
320             for (Parent parent = model.getParent(); version == null && model.getParent() != null;
321                 parent = model.getParent())
322             {
323                 version = parent.getVersion();
324             }
325             final Artifact artifact =
326                 this.artifactFactory.createArtifact(
327                     groupId,
328                     model.getArtifactId(),
329                     version,
330                     null,
331                     model.getPackaging());
332             final File pomParent = pom.getParentFile();
333             final String finalName = model.getArtifactId() + '-' + version;
334             final File explodedDirectory = new File(pomParent, "target/" + finalName);
335             final File artifactFile = new File(explodedDirectory + "." + model.getPackaging());
336             if (explodedDirectory.isDirectory() && artifactFile.exists() &&
337                 !finalName.equals(this.project.getBuild().getFinalName()))
338             {
339                 artifacts.add(artifact);
340                 artifact.setFile(artifactFile);
341             }
342             try
343             {
344                 freader.close();
345             }
346             catch (Exception ex)
347             {
348                 // Ignore
349             }
350         }
351         return artifacts;
352     }
353 }