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