1 package org.apache.maven.plugin.war.overlay;
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 java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.util.ListIterator;
26 import java.util.Set;
27
28 import org.apache.maven.artifact.Artifact;
29 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
30 import org.apache.maven.plugin.war.Overlay;
31 import org.apache.maven.project.MavenProject;
32 import org.codehaus.plexus.util.StringUtils;
33
34 /**
35 * Manages the overlays.
36 *
37 * @author Stephane Nicoll
38 * @version $Id: OverlayManager.html 935522 2015-01-08 20:15:45Z khmarbaise $
39 */
40 public class OverlayManager
41 {
42 private final List<Overlay> overlays;
43
44 private final MavenProject project;
45
46 private final List<Artifact> artifactsOverlays;
47
48 /**
49 * Creates a manager with the specified overlays.
50 * <p/>
51 * Note that the list is potentially updated by the manager so a new list is created based on the overlays.
52 *
53 * @param overlays the overlays
54 * @param project the maven project
55 * @param defaultIncludes the default includes to use
56 * @param defaultExcludes the default excludes to use
57 * @param currentProjectOverlay the overlay for the current project
58 * @throws InvalidOverlayConfigurationException if the config is invalid
59 */
60 public OverlayManager( List<Overlay> overlays, MavenProject project, String defaultIncludes,
61 String defaultExcludes, Overlay currentProjectOverlay )
62 throws InvalidOverlayConfigurationException
63 {
64 this.overlays = new ArrayList<Overlay>();
65 if ( overlays != null )
66 {
67 this.overlays.addAll( overlays );
68 }
69 this.project = project;
70
71 this.artifactsOverlays = getOverlaysAsArtifacts();
72
73 // Initialize
74 initialize( defaultIncludes, defaultExcludes, currentProjectOverlay );
75
76 }
77
78 /**
79 * Returns the resolved overlays.
80 *
81 * @return the overlays
82 */
83 public List<Overlay> getOverlays()
84 {
85 return overlays;
86 }
87
88 /**
89 * Returns the id of the resolved overlays.
90 *
91 * @return the overlay ids
92 */
93 public List<String> getOverlayIds()
94 {
95 final List<String> result = new ArrayList<String>();
96 for ( Overlay overlay : overlays )
97 {
98 result.add( overlay.getId() );
99 }
100 return result;
101
102 }
103
104 /**
105 * Initializes the manager and validates the overlays configuration.
106 *
107 * @param defaultIncludes the default includes to use
108 * @param defaultExcludes the default excludes to use
109 * @param currentProjectOverlay the overlay for the current project
110 * @throws InvalidOverlayConfigurationException if the configuration is invalid
111 */
112 void initialize( String defaultIncludes, String defaultExcludes, Overlay currentProjectOverlay )
113 throws InvalidOverlayConfigurationException
114 {
115
116 // Build the list of configured artifacts and makes sure that each overlay
117 // refer to a valid artifact
118 final List<Artifact> configuredWarArtifacts = new ArrayList<Artifact>();
119 final ListIterator<Overlay> it = overlays.listIterator();
120 while ( it.hasNext() )
121 {
122 Overlay overlay = it.next();
123 if ( overlay == null )
124 {
125 throw new InvalidOverlayConfigurationException( "overlay could not be null." );
126 }
127 // If it's the current project, return the project instance
128 if ( overlay.isCurrentProject() )
129 {
130 overlay = currentProjectOverlay;
131 it.set( overlay );
132 }
133 // default includes/excludes - only if the overlay uses the default settings
134 if ( Arrays.equals( Overlay.DEFAULT_INCLUDES, overlay.getIncludes() )
135 && Arrays.equals( Overlay.DEFAULT_EXCLUDES, overlay.getExcludes() ) )
136 {
137 overlay.setIncludes( defaultIncludes );
138 overlay.setExcludes( defaultExcludes );
139 }
140
141 final Artifact artifact = getAssociatedArtifact( overlay );
142 if ( artifact != null )
143 {
144 configuredWarArtifacts.add( artifact );
145 overlay.setArtifact( artifact );
146 }
147 }
148
149 // Build the list of missing overlays
150 for ( Artifact artifact : artifactsOverlays )
151 {
152 if ( !configuredWarArtifacts.contains( artifact ) )
153 {
154 // Add a default overlay for the given artifact which will be applied after
155 // the ones that have been configured
156 overlays.add( new DefaultOverlay( artifact, defaultIncludes, defaultExcludes ) );
157 }
158 }
159
160 // Final validation, make sure that the current project is in there. Otherwise add it first
161 for ( Overlay overlay : overlays )
162 {
163 if ( overlay.equals( currentProjectOverlay ) )
164 {
165 return;
166 }
167 }
168 overlays.add( 0, currentProjectOverlay );
169 }
170
171 /**
172 * Returns the Artifact associated to the specified overlay.
173 * <p/>
174 * If the overlay defines the current project, <tt>null</tt> is returned. If no artifact could not be found for the
175 * overlay a InvalidOverlayConfigurationException is thrown.
176 *
177 * @param overlay an overlay
178 * @return the artifact associated to the overlay
179 * @throws org.apache.maven.plugin.war.overlay.InvalidOverlayConfigurationException if the overlay does not have an
180 * associated artifact
181 */
182 Artifact getAssociatedArtifact( final Overlay overlay )
183 throws InvalidOverlayConfigurationException
184 {
185 if ( overlay.isCurrentProject() )
186 {
187 return null;
188 }
189
190 for ( Artifact artifact : artifactsOverlays )
191 {
192 // Handle classifier dependencies properly (clash management)
193 if ( compareOverlayWithArtifact( overlay, artifact ) )
194 {
195 return artifact;
196 }
197 }
198
199 // maybe its a project dependencies zip or an other type
200 @SuppressWarnings( "unchecked" )
201 Set<Artifact> projectArtifacts = this.project.getDependencyArtifacts();
202 if ( projectArtifacts != null )
203 {
204 for ( Artifact artifact : projectArtifacts )
205 {
206 if ( compareOverlayWithArtifact( overlay, artifact ) )
207 {
208 return artifact;
209 }
210 }
211 }
212 // CHECKSTYLE_OFF: LineLength
213 throw new InvalidOverlayConfigurationException( "overlay [" + overlay + "] is not a dependency of the project." );
214 // CHECKSTYLE_ON: LineLength
215
216 }
217
218 /**
219 * Compare groupId && artifactId && type && classifier.
220 *
221 * @param overlay the overlay
222 * @param artifact the artifact
223 * @return boolean true if equals
224 */
225 private boolean compareOverlayWithArtifact( Overlay overlay, Artifact artifact )
226 {
227 return ( StringUtils.equals( overlay.getGroupId(), artifact.getGroupId() )
228 && StringUtils.equals( overlay.getArtifactId(), artifact.getArtifactId() )
229 && StringUtils.equals( overlay.getType(), artifact.getType() )
230 // MWAR-241 Make sure to treat null and "" as equal when comparing the classifier
231 && StringUtils.equals( StringUtils.defaultString( overlay.getClassifier() ),
232 StringUtils.defaultString( artifact.getClassifier() ) ) );
233 }
234
235 /**
236 * Returns a list of WAR {@link org.apache.maven.artifact.Artifact} describing the overlays of the current project.
237 *
238 * @return the overlays as artifacts objects
239 */
240 private List<Artifact> getOverlaysAsArtifacts()
241 {
242 ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME );
243 @SuppressWarnings( "unchecked" )
244 final Set<Artifact> artifacts = project.getArtifacts();
245
246 final List<Artifact> result = new ArrayList<Artifact>();
247 for ( Artifact artifact : artifacts )
248 {
249 if ( !artifact.isOptional() && filter.include( artifact ) && ( "war".equals( artifact.getType() ) ) )
250 {
251 result.add( artifact );
252 }
253 }
254 return result;
255 }
256 }