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();//.getCompileClasspathElements();
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 // TODO find a solution to use a timestamp marker to not delete/extract all the time
92
93 File tmpExtractDatas =
94 new File( request.getMavenProject().getBuild().getDirectory(), "apache-tomcat-maven-plugin" );
95
96 if ( tmpExtractDatas.exists() )
97 {
98 deleteDirectory( tmpExtractDatas, request.getLog() );
99 }
100 tmpExtractDatas.mkdirs();
101
102 // add artifacts to loader
103 if ( request.getDependencies() != null )
104 {
105 for ( Artifact artifact : request.getDependencies() )
106 {
107 String scope = artifact.getScope();
108
109 // skip provided and test scoped artifacts
110 if ( !Artifact.SCOPE_PROVIDED.equals( scope ) && ( !Artifact.SCOPE_TEST.equals( scope )
111 || request.isUseTestClassPath() ) )
112 {
113 request.getLog().debug(
114 "add dependency to webapploader " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
115 + artifact.getVersion() + ":" + artifact.getScope() );
116 // we add artifact dependencies and projects from reactor if file (ie jar) as users can go to install/package phase
117 // so artifact.getFile is a file not a directory and not added when iterate on project.classPathElements
118 if ( !isInProjectReferences( artifact, request.getMavenProject() ) || artifact.getFile().isFile() )
119 {
120 String fileName = artifact.getFile().getName();
121 if ( !fileInClassLoaderEntries.contains( fileName ) )
122 {
123 classLoaderEntries.add( artifact.getFile().toURI().toString() );
124 fileInClassLoaderEntries.add( fileName );
125 }
126 }
127 else
128 {
129 request.getLog().debug(
130 "skip adding artifact " + artifact.getArtifactId() + " as it's in reactors" );
131
132 }
133 }
134
135 // in case of war dependency we must add /WEB-INF/lib/*.jar in entries and WEB-INF/classes
136 if ( "war".equals( artifact.getType() ) && request.isAddWarDependenciesInClassloader() )
137 {
138
139 File tmpDir = new File( tmpExtractDatas, artifact.getArtifactId() );
140
141 tmpDir.mkdirs();
142
143 tmpDirectories.add( tmpDir );
144
145 try
146 {
147 tmpDir.deleteOnExit();
148 File warFile = artifact.getFile();
149 UnArchiver unArchiver = archiverManager.getUnArchiver( "jar" );
150 unArchiver.setSourceFile( warFile );
151 unArchiver.setDestDirectory( tmpDir );
152 unArchiver.extract();
153
154 File libsDirectory = new File( tmpDir, "WEB-INF/lib" );
155 if ( libsDirectory.exists() )
156 {
157 String[] jars = libsDirectory.list( new FilenameFilter()
158 {
159 public boolean accept( File file, String s )
160 {
161 return s.endsWith( ".jar" );
162 }
163 } );
164 for ( String jar : jars )
165 {
166 File jarFile = new File( libsDirectory, jar );
167 if ( !fileInClassLoaderEntries.contains( jarFile.getName() ) )
168 {
169 classLoaderEntries.add( jarFile.toURI().toString() );
170 fileInClassLoaderEntries.add( jarFile.getName() );
171 }
172 else
173 {
174 request.getLog().debug( "skip adding file " + jarFile.getPath()
175 + " as it's already in classloader entries" );
176 }
177 }
178 }
179 File classesDirectory = new File( tmpDir, "WEB-INF/classes" );
180 if ( classesDirectory.exists() )
181 {
182 classLoaderEntries.add( classesDirectory.toURI().toString() );
183 }
184 }
185 catch ( NoSuchArchiverException e )
186 {
187 throw new TomcatRunException( e.getMessage(), e );
188 }
189 catch ( ArchiverException e )
190 {
191 request.getLog().error(
192 "fail to extract war file " + artifact.getFile() + ", reason:" + e.getMessage(), e );
193 throw new TomcatRunException( e.getMessage(), e );
194 }
195 }
196 }
197 }
198
199 return new ClassLoaderEntriesCalculatorResult( new ArrayList<String>( classLoaderEntries ), tmpDirectories );
200
201 }
202
203 private void deleteDirectory( File directory, Log log )
204 throws TomcatRunException
205 {
206 try
207 {
208 FileUtils.deleteDirectory( directory );
209 }
210 catch ( IOException e )
211 {
212 log.error( "fail to delete directory file " + directory + ", reason:" + e.getMessage(), e );
213 throw new TomcatRunException( e.getMessage(), e );
214 }
215 }
216
217 protected boolean isInProjectReferences( Artifact artifact, MavenProject project )
218 {
219 if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() )
220 {
221 return false;
222 }
223 @SuppressWarnings ("unchecked") Collection<MavenProject> mavenProjects =
224 project.getProjectReferences().values();
225 for ( MavenProject mavenProject : mavenProjects )
226 {
227 if ( StringUtils.equals( mavenProject.getId(), artifact.getId() ) )
228 {
229 return true;
230 }
231 }
232 return false;
233 }
234 }