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 }