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