1
2
3
4
5
6
7
8
9
10
11 package com.simpligility.maven.plugins.android.phase_prebuild;
12
13 import com.simpligility.maven.plugins.android.common.AndroidExtension;
14 import com.simpligility.maven.plugins.android.common.ArtifactResolverHelper;
15 import com.simpligility.maven.plugins.android.common.DependencyResolver;
16 import com.simpligility.maven.plugins.android.common.PomConfigurationHelper;
17 import com.simpligility.maven.plugins.android.common.UnpackedLibHelper;
18 import org.apache.maven.AbstractMavenLifecycleParticipant;
19 import org.apache.maven.MavenExecutionException;
20 import org.apache.maven.artifact.Artifact;
21 import org.apache.maven.artifact.resolver.ArtifactResolver;
22 import org.apache.maven.execution.MavenSession;
23 import org.apache.maven.model.Dependency;
24 import org.apache.maven.plugin.MojoExecutionException;
25 import org.apache.maven.project.MavenProject;
26 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
27 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
28 import org.codehaus.plexus.component.annotations.Component;
29 import org.codehaus.plexus.component.annotations.Requirement;
30 import org.codehaus.plexus.logging.Logger;
31
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.util.Enumeration;
36 import java.util.List;
37 import java.util.Set;
38 import java.util.regex.Pattern;
39 import java.util.zip.ZipEntry;
40 import java.util.zip.ZipException;
41 import java.util.zip.ZipFile;
42 import java.util.zip.ZipOutputStream;
43
44
45
46
47
48
49
50
51
52 @Component( role = AbstractMavenLifecycleParticipant.class, hint = "default" )
53 public final class ClasspathModifierLifecycleParticipant extends AbstractMavenLifecycleParticipant
54 {
55
56
57
58
59
60 private static final String INCLUDE_FROM_APKLIB_PARAM = "includeLibsJarsFromApklib";
61
62
63
64
65
66 private static final String INCLUDE_FROM_AAR_PARAM = "includeLibsJarsFromAar";
67
68
69
70
71
72
73 private static final String DISABLE_CONFLICTING_DEPENDENCIES_WARNING_PARAM
74 = "disableConflictingDependenciesWarning";
75 private static final boolean INCLUDE_FROM_APKLIB_DEFAULT = false;
76 private static final boolean INCLUDE_FROM_AAR_DEFAULT = true;
77 private static final boolean DISABLE_CONFLICTING_DEPENDENCIES_WARNING_DEFAULT = false;
78
79
80
81
82
83 private static final String UNPACKED_LIBS_FOLDER_PARAM = "unpackedLibsFolder";
84
85 @Requirement
86 private ArtifactResolver artifactResolver;
87
88 @Requirement( hint = "default" )
89 private DependencyGraphBuilder dependencyGraphBuilder;
90
91 @Requirement
92 private Logger log;
93
94 private boolean addedJarFromLibs = false;
95
96 @Override
97 public void afterProjectsRead( MavenSession session ) throws MavenExecutionException
98 {
99 log.debug( "" );
100 log.debug( "ClasspathModifierLifecycleParticipant#afterProjectsRead - start" );
101 log.debug( "" );
102
103 log.debug( "CurrentProject=" + session.getCurrentProject() );
104 final List<MavenProject> projects = session.getProjects();
105 final DependencyResolver dependencyResolver = new DependencyResolver( log, dependencyGraphBuilder );
106 final ArtifactResolverHelper artifactResolverHelper = new ArtifactResolverHelper( artifactResolver, log );
107
108 for ( MavenProject project : projects )
109 {
110 log.debug( "" );
111 log.debug( "project=" + project.getArtifact() );
112
113 if ( ! AndroidExtension.isAndroidPackaging( project.getPackaging() ) )
114 {
115 continue;
116 }
117
118 final String unpackedLibsFolder
119 = getMojoConfigurationParameter( project, UNPACKED_LIBS_FOLDER_PARAM, null );
120 final UnpackedLibHelper helper = new UnpackedLibHelper( artifactResolverHelper, project, log,
121 unpackedLibsFolder == null ? null : new File( unpackedLibsFolder )
122 );
123
124 final Set<Artifact> artifacts;
125
126
127
128 final ClassLoader projectClassLoader = ( project.getClassRealm() != null )
129 ? project.getClassRealm()
130 : Thread.currentThread().getContextClassLoader();
131
132 final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
133 try
134 {
135 Thread.currentThread().setContextClassLoader( projectClassLoader );
136 artifacts = dependencyResolver.getProjectDependenciesFor( project, session );
137 }
138 catch ( DependencyGraphBuilderException e )
139 {
140
141 continue;
142 }
143 finally
144 {
145 Thread.currentThread().setContextClassLoader( originalClassLoader );
146 }
147
148 boolean includeFromAar = getMojoConfigurationParameter( project, INCLUDE_FROM_AAR_PARAM,
149 INCLUDE_FROM_AAR_DEFAULT );
150 boolean includeFromApklib = getMojoConfigurationParameter( project, INCLUDE_FROM_APKLIB_PARAM,
151 INCLUDE_FROM_APKLIB_DEFAULT );
152 boolean disableConflictingDependenciesWarning = getMojoConfigurationParameter( project,
153 DISABLE_CONFLICTING_DEPENDENCIES_WARNING_PARAM, DISABLE_CONFLICTING_DEPENDENCIES_WARNING_DEFAULT );
154
155 log.debug( "projects deps: : " + artifacts );
156
157 if ( !disableConflictingDependenciesWarning )
158 {
159 ProvidedDependencyChecker checker = new ProvidedDependencyChecker();
160 checker.checkProvidedDependencies( artifacts, log );
161 }
162
163 for ( Artifact artifact : artifacts )
164 {
165 final String type = artifact.getType();
166 if ( type.equals( AndroidExtension.AAR ) )
167 {
168
169
170
171 addClassesToClasspath( helper, project, artifact );
172
173
174
175 if ( includeFromAar )
176 {
177
178 addLibsJarsToClassPath( helper, project, artifact );
179 }
180 }
181 else if ( type.equals( AndroidExtension.APK ) )
182 {
183
184
185
186 addClassesToClasspath( helper, project, artifact );
187 }
188 else if ( type.equals( AndroidExtension.APKLIB ) )
189 {
190 if ( includeFromApklib )
191 {
192
193 addLibsJarsToClassPath( helper, project, artifact );
194 }
195 }
196 }
197 }
198
199 if ( addedJarFromLibs )
200 {
201 log.warn(
202 "Transitive dependencies should really be provided by Maven dependency management.\n"
203 + " We suggest you to ask the above providers to package their component properly.\n"
204 + " Things may break at compile and/or runtime due to multiple copies of incompatible libraries." );
205 }
206 log.debug( "" );
207 log.debug( "ClasspathModifierLifecycleParticipant#afterProjectsRead - finish" );
208 }
209
210 private String getMojoConfigurationParameter( MavenProject project, String name, String defaultValue )
211 {
212 String value = PomConfigurationHelper.getPluginConfigParameter( project,
213 name, defaultValue );
214 log.debug( name + " set to " + value );
215 return value;
216 }
217
218 private boolean getMojoConfigurationParameter( MavenProject project, String name, boolean defaultValue )
219 {
220 return Boolean.valueOf( getMojoConfigurationParameter( project, name, Boolean.toString( defaultValue ) ) );
221 }
222
223
224
225
226 private void addLibsJarsToClassPath( UnpackedLibHelper helper, MavenProject project, Artifact artifact )
227 throws MavenExecutionException
228 {
229 try
230 {
231 final File unpackLibFolder = helper.getUnpackedLibFolder( artifact );
232 final File artifactFile = helper.getArtifactToFile( artifact );
233 ZipFile zipFile = new ZipFile( artifactFile );
234 Enumeration enumeration = zipFile.entries();
235 while ( enumeration.hasMoreElements() )
236 {
237 ZipEntry entry = ( ZipEntry ) enumeration.nextElement();
238 String entryName = entry.getName();
239
240
241 if ( Pattern.matches( "^libs/.+\\.jar$", entryName ) )
242 {
243 final File libsJarFile = new File( unpackLibFolder, entryName );
244 log.warn( "Adding jar from libs folder to classpath: " + libsJarFile );
245
246
247
248 if ( !libsJarFile.getParentFile().exists() )
249 {
250 libsJarFile.getParentFile().mkdirs();
251 }
252 libsJarFile.createNewFile();
253
254
255 final Dependency dependency =
256 createSystemScopeDependency( artifact, libsJarFile, libsJarFile.getName() );
257
258 project.getModel().addDependency( dependency );
259 addedJarFromLibs = true;
260 }
261 }
262 }
263 catch ( MojoExecutionException e )
264 {
265 log.debug( "Error extract jars" );
266 }
267 catch ( ZipException e )
268 {
269 log.debug( "Error" );
270 }
271 catch ( IOException e )
272 {
273 log.debug( "Error" );
274 }
275 }
276
277
278
279
280 private void addClassesToClasspath( UnpackedLibHelper helper, MavenProject project, Artifact artifact )
281 throws MavenExecutionException
282 {
283
284
285 final File classesJar = helper.getUnpackedClassesJar( artifact );
286 log.debug( "Adding to classpath : " + classesJar );
287
288
289
290 if ( !classesJar.exists() )
291 {
292 classesJar.getParentFile().mkdirs();
293 try
294 {
295 final ZipOutputStream zipOutputStream = new ZipOutputStream( new FileOutputStream( classesJar ) );
296 zipOutputStream.putNextEntry( new ZipEntry( "dummy" ) );
297 zipOutputStream.close();
298 log.debug( "Created dummy " + classesJar.getName() + " exist=" + classesJar.exists() );
299 }
300 catch ( IOException e )
301 {
302 throw new MavenExecutionException( "Could not add " + classesJar.getName() + " as dependency", e );
303 }
304 }
305
306
307
308
309 final Dependency dependency = createSystemScopeDependency( artifact, classesJar, null );
310 final Dependency providedJar = findProvidedDependencies( dependency, project );
311 if ( providedJar != null )
312 {
313 project.getModel().removeDependency( providedJar );
314 }
315 project.getModel().addDependency( dependency );
316 }
317
318 private Dependency createSystemScopeDependency( Artifact artifact, File location, String suffix )
319 {
320 String artifactId = artifact.getArtifactId();
321 if ( suffix != null )
322 {
323 artifactId += "_" + suffix;
324 }
325 final Dependency dependency = new Dependency();
326 dependency.setGroupId( artifact.getGroupId() );
327 dependency.setArtifactId( artifactId );
328 dependency.setVersion( artifact.getBaseVersion() );
329 dependency.setScope( Artifact.SCOPE_SYSTEM );
330 dependency.setSystemPath( location.getAbsolutePath() );
331 return dependency;
332 }
333
334 private Dependency findProvidedDependencies( Dependency dexDependency, MavenProject project )
335 {
336 for ( Dependency dependency : project.getDependencies() )
337 {
338 if ( dependency.getScope().equals( Artifact.SCOPE_PROVIDED ) )
339 {
340 if ( dependency.getArtifactId().equals( dexDependency.getArtifactId() )
341 && dependency.getGroupId().equals( dexDependency.getGroupId() )
342 && dependency.getType().equals( dexDependency.getType() )
343 && dependency.getVersion().equals( dexDependency.getVersion() ) )
344 {
345 return dependency;
346 }
347 }
348 }
349 return null;
350
351 }
352
353 }
354