View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.maven;
6   
7   import com.jcabi.log.Logger;
8   import com.qulice.spi.ResourceValidator;
9   import com.qulice.spi.ValidationException;
10  import com.qulice.spi.Validator;
11  import com.qulice.spi.Violation;
12  import java.io.File;
13  import java.util.Collection;
14  import java.util.Collections;
15  import java.util.LinkedList;
16  import java.util.Locale;
17  import java.util.concurrent.Callable;
18  import java.util.concurrent.ExecutionException;
19  import java.util.concurrent.ExecutorService;
20  import java.util.concurrent.Executors;
21  import java.util.concurrent.Future;
22  import java.util.concurrent.TimeUnit;
23  import java.util.concurrent.TimeoutException;
24  import org.apache.maven.plugin.MojoFailureException;
25  import org.apache.maven.plugins.annotations.LifecyclePhase;
26  import org.apache.maven.plugins.annotations.Mojo;
27  import org.apache.maven.plugins.annotations.ResolutionScope;
28  
29  /**
30   * Check the project and find all possible violations.
31   *
32   * @since 0.3
33   */
34  @Mojo(name = "check", defaultPhase = LifecyclePhase.VERIFY,
35      requiresDependencyResolution = ResolutionScope.TEST,
36      threadSafe = true)
37  public final class CheckMojo extends AbstractQuliceMojo {
38  
39      /**
40       * Executors for validators.
41       */
42      private final ExecutorService executors =
43          Executors.newFixedThreadPool(5);
44  
45      /**
46       * Provider of validators.
47       */
48      private ValidatorsProvider provider =
49          new DefaultValidatorsProvider(this.env());
50  
51      @Override
52      public void doExecute() throws MojoFailureException {
53          try {
54              this.run();
55          } catch (final ValidationException ex) {
56              Logger.info(
57                  this,
58                  "Read our quality policy: http://www.qulice.com/quality.html"
59              );
60              throw new MojoFailureException("Failure", ex);
61          }
62      }
63  
64      /**
65       * Set provider of validators.
66       * @param prov The provider
67       */
68      public void setValidatorsProvider(final ValidatorsProvider prov) {
69          this.provider = prov;
70      }
71  
72      /**
73       * Run them all.
74       * @throws ValidationException If any of them fail
75       */
76      private void run() throws ValidationException {
77          final LinkedList<Violation> results = new LinkedList<>();
78          final MavenEnvironment env = this.env();
79          final Collection<File> files = env.files("*.*");
80          if (!files.isEmpty()) {
81              final Collection<ResourceValidator> validators =
82                  this.provider.externalResource();
83              final Collection<Future<Collection<Violation>>> futures =
84                  this.submit(env, files, validators);
85              for (final Future<Collection<Violation>> future : futures) {
86                  try {
87                      results.addAll(future.get(10L, TimeUnit.MINUTES));
88                  } catch (final InterruptedException ex) {
89                      Thread.currentThread().interrupt();
90                      throw new IllegalStateException(ex);
91                  } catch (final ExecutionException | TimeoutException ex) {
92                      throw new IllegalStateException(ex);
93                  }
94              }
95              Collections.sort(results);
96              for (final Violation result : results) {
97                  Logger.info(
98                      this,
99                      "%s: %s[%s]: %s (%s)",
100                     result.validator(),
101                     result.file().replace(
102                         String.format(
103                             "%s/", this.session().getExecutionRootDirectory()
104                         ),
105                         ""
106                     ),
107                     result.lines(),
108                     result.message(),
109                     result.name()
110                 );
111             }
112         }
113         if (!results.isEmpty()) {
114             throw new ValidationException(
115                 String.format("There are %d violations", results.size())
116            );
117         }
118         for (final Validator validator : this.provider.external()) {
119             Logger.info(this, "Starting %s validator", validator.name());
120             validator.validate(env);
121             Logger.info(this, "Finishing %s validator", validator.name());
122         }
123         for (final MavenValidator validator : this.provider.internal()) {
124             validator.validate(env);
125         }
126     }
127 
128     /**
129      * Submit validators to executor.
130      * @param env Maven environment
131      * @param files List of files to validate
132      * @param validators Validators to use
133      * @return List of futures
134      */
135     @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
136     private Collection<Future<Collection<Violation>>> submit(
137         final MavenEnvironment env, final Collection<File> files,
138         final Collection<ResourceValidator> validators) {
139         final Collection<Future<Collection<Violation>>> futures =
140             new LinkedList<>();
141         for (final ResourceValidator validator : validators) {
142             futures.add(
143                 this.executors.submit(
144                     new ValidatorCallable(validator, env, files)
145                 )
146             );
147         }
148         return futures;
149     }
150 
151     /**
152      * Filter files based on excludes.
153      * @param env Maven environment
154      * @param files Files to exclude
155      * @param validator Validator to use
156      * @return Filtered files
157      */
158     private static Collection<File> filter(final MavenEnvironment env,
159         final Collection<File> files, final ResourceValidator validator) {
160         final Collection<File> filtered = new LinkedList<>();
161         for (final File file : files) {
162             if (
163                 !env.exclude(
164                     validator.name().toLowerCase(Locale.ENGLISH),
165                     file.toString()
166                 )
167             ) {
168                 filtered.add(file);
169             }
170         }
171         return filtered;
172     }
173 
174     /**
175      * Callable for validators.
176      *
177      * @since 0.1
178      */
179     private static class ValidatorCallable
180         implements Callable<Collection<Violation>> {
181         /**
182          * Validator to use.
183          */
184         private final ResourceValidator validator;
185 
186         /**
187          * Maven environment.
188          */
189         private final MavenEnvironment env;
190 
191         /**
192          * List of files to validate.
193          */
194         private final Collection<File> files;
195 
196         /**
197          * Constructor.
198          * @param validator Validator to use
199          * @param env Maven environment
200          * @param files List of files to validate
201          */
202         ValidatorCallable(final ResourceValidator validator,
203             final MavenEnvironment env, final Collection<File> files) {
204             this.validator = validator;
205             this.env = env;
206             this.files = files;
207         }
208 
209         @Override
210         public Collection<Violation> call() {
211             return this.validator.validate(
212                 CheckMojo.filter(this.env, this.files, this.validator)
213             );
214         }
215     }
216 }