View Javadoc
1   /*
2    * Copyright (c) 2011-2025 Yegor Bugayenko
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.maven;
32  
33  import com.jcabi.log.Logger;
34  import com.qulice.spi.ResourceValidator;
35  import com.qulice.spi.ValidationException;
36  import com.qulice.spi.Validator;
37  import com.qulice.spi.Violation;
38  import java.io.File;
39  import java.util.Collection;
40  import java.util.Collections;
41  import java.util.LinkedList;
42  import java.util.Locale;
43  import java.util.concurrent.Callable;
44  import java.util.concurrent.ExecutionException;
45  import java.util.concurrent.ExecutorService;
46  import java.util.concurrent.Executors;
47  import java.util.concurrent.Future;
48  import java.util.concurrent.TimeUnit;
49  import java.util.concurrent.TimeoutException;
50  import org.apache.maven.plugin.MojoFailureException;
51  import org.apache.maven.plugins.annotations.LifecyclePhase;
52  import org.apache.maven.plugins.annotations.Mojo;
53  import org.apache.maven.plugins.annotations.ResolutionScope;
54  
55  /**
56   * Check the project and find all possible violations.
57   *
58   * @since 0.3
59   */
60  @Mojo(name = "check", defaultPhase = LifecyclePhase.VERIFY,
61      requiresDependencyResolution = ResolutionScope.TEST,
62      threadSafe = true)
63  public final class CheckMojo extends AbstractQuliceMojo {
64  
65      /**
66       * Executors for validators.
67       */
68      private final ExecutorService executors =
69          Executors.newFixedThreadPool(5);
70  
71      /**
72       * Provider of validators.
73       */
74      private ValidatorsProvider provider =
75          new DefaultValidatorsProvider(this.env());
76  
77      @Override
78      public void doExecute() throws MojoFailureException {
79          try {
80              this.run();
81          } catch (final ValidationException ex) {
82              Logger.info(
83                  this,
84                  "Read our quality policy: http://www.qulice.com/quality.html"
85              );
86              throw new MojoFailureException("Failure", ex);
87          }
88      }
89  
90      /**
91       * Set provider of validators.
92       * @param prov The provider
93       */
94      public void setValidatorsProvider(final ValidatorsProvider prov) {
95          this.provider = prov;
96      }
97  
98      /**
99       * Run them all.
100      * @throws ValidationException If any of them fail
101      */
102     private void run() throws ValidationException {
103         final LinkedList<Violation> results = new LinkedList<>();
104         final MavenEnvironment env = this.env();
105         final Collection<File> files = env.files("*.*");
106         if (!files.isEmpty()) {
107             final Collection<ResourceValidator> validators =
108                 this.provider.externalResource();
109             final Collection<Future<Collection<Violation>>> futures =
110                 this.submit(env, files, validators);
111             for (final Future<Collection<Violation>> future : futures) {
112                 try {
113                     results.addAll(future.get(10L, TimeUnit.MINUTES));
114                 } catch (final InterruptedException ex) {
115                     Thread.currentThread().interrupt();
116                     throw new IllegalStateException(ex);
117                 } catch (final ExecutionException | TimeoutException ex) {
118                     throw new IllegalStateException(ex);
119                 }
120             }
121             Collections.sort(results);
122             for (final Violation result : results) {
123                 Logger.info(
124                     this,
125                     "%s: %s[%s]: %s (%s)",
126                     result.validator(),
127                     result.file().replace(
128                         String.format(
129                             "%s/", this.session().getExecutionRootDirectory()
130                         ),
131                         ""
132                     ),
133                     result.lines(),
134                     result.message(),
135                     result.name()
136                 );
137             }
138         }
139         if (!results.isEmpty()) {
140             throw new ValidationException(
141                 String.format("There are %d violations", results.size())
142            );
143         }
144         for (final Validator validator : this.provider.external()) {
145             Logger.info(this, "Starting %s validator", validator.name());
146             validator.validate(env);
147             Logger.info(this, "Finishing %s validator", validator.name());
148         }
149         for (final MavenValidator validator : this.provider.internal()) {
150             validator.validate(env);
151         }
152     }
153 
154     /**
155      * Submit validators to executor.
156      * @param env Maven environment
157      * @param files List of files to validate
158      * @param validators Validators to use
159      * @return List of futures
160      */
161     @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
162     private Collection<Future<Collection<Violation>>> submit(
163         final MavenEnvironment env, final Collection<File> files,
164         final Collection<ResourceValidator> validators) {
165         final Collection<Future<Collection<Violation>>> futures =
166             new LinkedList<>();
167         for (final ResourceValidator validator : validators) {
168             futures.add(
169                 this.executors.submit(
170                     new ValidatorCallable(validator, env, files)
171                 )
172             );
173         }
174         return futures;
175     }
176 
177     /**
178      * Filter files based on excludes.
179      * @param env Maven environment
180      * @param files Files to exclude
181      * @param validator Validator to use
182      * @return Filtered files
183      */
184     private static Collection<File> filter(final MavenEnvironment env,
185         final Collection<File> files, final ResourceValidator validator) {
186         final Collection<File> filtered = new LinkedList<>();
187         for (final File file : files) {
188             if (
189                 !env.exclude(
190                     validator.name().toLowerCase(Locale.ENGLISH),
191                     file.toString()
192                 )
193             ) {
194                 filtered.add(file);
195             }
196         }
197         return filtered;
198     }
199 
200     /**
201      * Callable for validators.
202      *
203      * @since 0.1
204      */
205     private static class ValidatorCallable
206         implements Callable<Collection<Violation>> {
207         /**
208          * Validator to use.
209          */
210         private final ResourceValidator validator;
211 
212         /**
213          * Maven environment.
214          */
215         private final MavenEnvironment env;
216 
217         /**
218          * List of files to validate.
219          */
220         private final Collection<File> files;
221 
222         /**
223          * Constructor.
224          * @param validator Validator to use
225          * @param env Maven environment
226          * @param files List of files to validate
227          */
228         ValidatorCallable(final ResourceValidator validator,
229             final MavenEnvironment env, final Collection<File> files) {
230             this.validator = validator;
231             this.env = env;
232             this.files = files;
233         }
234 
235         @Override
236         public Collection<Violation> call() {
237             return this.validator.validate(
238                 CheckMojo.filter(this.env, this.files, this.validator)
239             );
240         }
241     }
242 }