View Javadoc
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 925069 2014-10-08 17:03:57Z 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         throw new InvalidOverlayConfigurationException( "overlay [" + overlay + "] is not a dependency of the project." );
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         @SuppressWarnings( "unchecked" )
242         final Set<Artifact> artifacts = project.getArtifacts();
243 
244         final List<Artifact> result = new ArrayList<Artifact>();
245         for ( Artifact artifact : artifacts )
246         {
247             if ( !artifact.isOptional() && filter.include( artifact ) && ( "war".equals( artifact.getType() ) ) )
248             {
249                 result.add( artifact );
250             }
251         }
252         return result;
253     }
254 }