View Javadoc

1   package org.apache.tomcat.maven.common.run;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.io.FileUtils;
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.DependencyResolutionRequiredException;
25  import org.apache.maven.plugin.logging.Log;
26  import org.apache.maven.project.MavenProject;
27  import org.codehaus.plexus.archiver.ArchiverException;
28  import org.codehaus.plexus.archiver.UnArchiver;
29  import org.codehaus.plexus.archiver.manager.ArchiverManager;
30  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
31  import org.codehaus.plexus.component.annotations.Component;
32  import org.codehaus.plexus.component.annotations.Requirement;
33  import org.codehaus.plexus.util.StringUtils;
34  
35  import java.io.File;
36  import java.io.FilenameFilter;
37  import java.io.IOException;
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.LinkedHashSet;
41  import java.util.List;
42  import java.util.Set;
43  
44  /**
45   * @author Olivier Lamy
46   * @since 2.0
47   */
48  @Component (role = ClassLoaderEntriesCalculator.class)
49  public class DefaultClassLoaderEntriesCalculator
50      implements ClassLoaderEntriesCalculator
51  {
52  
53      @Requirement
54      private ArchiverManager archiverManager;
55  
56  
57      public ClassLoaderEntriesCalculatorResult calculateClassPathEntries( ClassLoaderEntriesCalculatorRequest request )
58          throws TomcatRunException
59      {
60          Set<String> classLoaderEntries = new LinkedHashSet<String>();
61  
62          List<String> fileInClassLoaderEntries = new ArrayList<String>();
63  
64          List<File> tmpDirectories = new ArrayList<File>();
65  
66          // add classes directories to loader
67          try
68          {
69              @SuppressWarnings ("unchecked") List<String> classPathElements = request.isUseTestClassPath()
70                  ? request.getMavenProject().getTestClasspathElements()
71                  : request.getMavenProject().getRuntimeClasspathElements();
72              if ( classPathElements != null )
73              {
74                  for ( String classPathElement : classPathElements )
75                  {
76                      File classPathElementFile = new File( classPathElement );
77                      if ( classPathElementFile.isDirectory() )
78                      {
79                          request.getLog().debug(
80                              "adding classPathElementFile " + classPathElementFile.toURI().toString() );
81                          classLoaderEntries.add( classPathElementFile.toURI().toString() );
82                      }
83                  }
84              }
85          }
86          catch ( DependencyResolutionRequiredException e )
87          {
88              throw new TomcatRunException( e.getMessage(), e );
89          }
90  
91          File tmpExtractDatas =
92              new File( request.getMavenProject().getBuild().getDirectory(), "apache-tomcat-maven-plugin" );
93  
94          tmpExtractDatas.mkdirs();
95  
96          // add artifacts to loader
97          if ( request.getDependencies() != null )
98          {
99              for ( Artifact artifact : request.getDependencies() )
100             {
101                 String scope = artifact.getScope();
102 
103                 // skip provided and test scoped artifacts
104                 if ( !Artifact.SCOPE_PROVIDED.equals( scope ) && ( !Artifact.SCOPE_TEST.equals( scope )
105                     || request.isUseTestClassPath() ) )
106                 {
107                     request.getLog().debug(
108                         "add dependency to webapploader " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
109                             + artifact.getVersion() + ":" + artifact.getScope() );
110                     // we add artifact dependencies and projects from reactor if file (ie jar) as users can go to install/package phase
111                     // so artifact.getFile is a file not a directory and not added when iterate on project.classPathElements
112                     if ( !isInProjectReferences( artifact, request.getMavenProject() ) ||  artifact.getFile().isFile() )
113                     {
114                         String fileName = artifact.getGroupId() + "-" + artifact.getFile().getName();
115                         if ( !fileInClassLoaderEntries.contains( fileName ) )
116                         {
117                             classLoaderEntries.add( artifact.getFile().toURI().toString() );
118                             fileInClassLoaderEntries.add( fileName );
119                         }
120                     }
121                     else
122                     {
123                         request.getLog().debug(
124                             "skip adding artifact " + artifact.getArtifactId() + " as it's in reactors" );
125 
126                     }
127                 }
128 
129                 // in case of war dependency we must add /WEB-INF/lib/*.jar in entries and WEB-INF/classes
130                 if ( "war".equals( artifact.getType() ) && request.isAddWarDependenciesInClassloader() )
131                 {
132 
133                     File tmpDir = new File( tmpExtractDatas, artifact.getArtifactId() );
134 
135                     boolean existed = !tmpDir.mkdirs();
136                     // does a directory for this artifact already exist?
137                     if (existed)
138                     {
139                         // check timestamp to see if artifact is newer than extracted directory
140                         long dirLastMod = tmpDir.lastModified();
141                         long warLastMod = artifact.getFile().lastModified();
142 
143                         if (warLastMod == 0L || warLastMod > dirLastMod)
144                         {
145                             request.getLog().debug(
146                                 "re-exploding artifact " + artifact.getArtifactId() + " due to newer WAR");
147 
148                             deleteDirectory( tmpDir, request.getLog() );
149                             tmpDir = new File( tmpExtractDatas, artifact.getArtifactId() );
150                             tmpDir.mkdirs();
151                             existed = false;
152                         }
153                         else
154                         {
155                             request.getLog().debug(
156                                 "using existing exploded war for artifact " + artifact.getArtifactId());
157                         }
158                     }
159 
160                     tmpDirectories.add( tmpDir );
161 
162                     try
163                     {
164                         // explode the archive if it is not already exploded
165                         if (!existed)
166                         {
167                             File warFile = artifact.getFile();
168                             UnArchiver unArchiver = archiverManager.getUnArchiver( "jar" );
169                             unArchiver.setSourceFile( warFile );
170                             unArchiver.setDestDirectory( tmpDir );
171                             unArchiver.extract();
172                         }
173 
174                         File libsDirectory = new File( tmpDir, "WEB-INF/lib" );
175                         if ( libsDirectory.exists() )
176                         {
177                             String[] jars = libsDirectory.list( new FilenameFilter()
178                             {
179                                 public boolean accept( File file, String s )
180                                 {
181                                     return s.endsWith( ".jar" );
182                                 }
183                             } );
184                             for ( String jar : jars )
185                             {
186                                 File jarFile = new File( libsDirectory, jar );
187                                 if ( !fileInClassLoaderEntries.contains( jarFile.getName() ) )
188                                 {
189                                     classLoaderEntries.add( jarFile.toURI().toString() );
190                                     fileInClassLoaderEntries.add( jarFile.getName() );
191                                 }
192                                 else
193                                 {
194                                     request.getLog().debug( "skip adding file " + jarFile.getPath()
195                                                                 + " as it's already in classloader entries" );
196                                 }
197                             }
198                         }
199                         File classesDirectory = new File( tmpDir, "WEB-INF/classes" );
200                         if ( classesDirectory.exists() )
201                         {
202                             classLoaderEntries.add( classesDirectory.toURI().toString() );
203                         }
204                     }
205                     catch ( NoSuchArchiverException e )
206                     {
207                         throw new TomcatRunException( e.getMessage(), e );
208                     }
209                     catch ( ArchiverException e )
210                     {
211                         request.getLog().error(
212                             "fail to extract war file " + artifact.getFile() + ", reason:" + e.getMessage(), e );
213                         throw new TomcatRunException( e.getMessage(), e );
214                     }
215                 }
216             }
217         }
218 
219         return new ClassLoaderEntriesCalculatorResult( new ArrayList<String>( classLoaderEntries ), tmpDirectories );
220 
221     }
222 
223     private void deleteDirectory( File directory, Log log )
224         throws TomcatRunException
225     {
226         try
227         {
228             FileUtils.deleteDirectory( directory );
229         }
230         catch ( IOException e )
231         {
232             log.error( "fail to delete directory file " + directory + ", reason:" + e.getMessage(), e );
233             throw new TomcatRunException( e.getMessage(), e );
234         }
235     }
236 
237     protected boolean isInProjectReferences( Artifact artifact, MavenProject project )
238     {
239         if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() )
240         {
241             return false;
242         }
243         @SuppressWarnings ("unchecked") Collection<MavenProject> mavenProjects =
244             project.getProjectReferences().values();
245         for ( MavenProject mavenProject : mavenProjects )
246         {
247             if ( StringUtils.equals( mavenProject.getId(), artifact.getId() ) )
248             {
249                 return true;
250             }
251         }
252         return false;
253     }
254 }