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 }