View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
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  public final class CheckMojo extends AbstractQuliceMojo {
63  
64      /**
65       * Executors for validators.
66       */
67      private final ExecutorService executors =
68          Executors.newFixedThreadPool(5);
69  
70      /**
71       * Provider of validators.
72       */
73      private ValidatorsProvider provider =
74          new DefaultValidatorsProvider(this.env());
75  
76      @Override
77      public void doExecute() throws MojoFailureException {
78          try {
79              this.run();
80          } catch (final ValidationException ex) {
81              Logger.info(
82                  this,
83                  "Read our quality policy: http://www.qulice.com/quality.html"
84              );
85              throw new MojoFailureException("Failure", ex);
86          }
87      }
88  
89      /**
90       * Set provider of validators.
91       * @param prov The provider
92       */
93      public void setValidatorsProvider(final ValidatorsProvider prov) {
94          this.provider = prov;
95      }
96  
97      /**
98       * Run them all.
99       * @throws ValidationException If any of them fail
100      */
101     private void run() throws ValidationException {
102         final LinkedList<Violation> results = new LinkedList<>();
103         final MavenEnvironment env = this.env();
104         final Collection<File> files = env.files("*.*");
105         if (!files.isEmpty()) {
106             final Collection<ResourceValidator> validators =
107                 this.provider.externalResource();
108             final Collection<Future<Collection<Violation>>> futures =
109                 this.submit(env, files, validators);
110             for (final Future<Collection<Violation>> future : futures) {
111                 try {
112                     results.addAll(future.get(10L, TimeUnit.MINUTES));
113                 } catch (final InterruptedException ex) {
114                     Thread.currentThread().interrupt();
115                     throw new IllegalStateException(ex);
116                 } catch (final ExecutionException | TimeoutException ex) {
117                     throw new IllegalStateException(ex);
118                 }
119             }
120             Collections.sort(results);
121             for (final Violation result : results) {
122                 Logger.info(
123                     this,
124                     "%s: %s[%s]: %s (%s)",
125                     result.validator(),
126                     result.file().replace(
127                         String.format(
128                             "%s/", this.session().getExecutionRootDirectory()
129                         ),
130                         ""
131                     ),
132                     result.lines(),
133                     result.message(),
134                     result.name()
135                 );
136             }
137         }
138         if (!results.isEmpty()) {
139             throw new ValidationException(
140                 String.format("There are %d violations", results.size())
141            );
142         }
143         for (final Validator validator : this.provider.external()) {
144             Logger.info(this, "Starting %s validator", validator.name());
145             validator.validate(env);
146             Logger.info(this, "Finishing %s validator", validator.name());
147         }
148         for (final MavenValidator validator : this.provider.internal()) {
149             validator.validate(env);
150         }
151     }
152 
153     /**
154      * Submit validators to executor.
155      * @param env Maven environment
156      * @param files List of files to validate
157      * @param validators Validators to use
158      * @return List of futures
159      */
160     @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
161     private Collection<Future<Collection<Violation>>> submit(
162         final MavenEnvironment env, final Collection<File> files,
163         final Collection<ResourceValidator> validators) {
164         final Collection<Future<Collection<Violation>>> futures =
165             new LinkedList<>();
166         for (final ResourceValidator validator : validators) {
167             futures.add(
168                 this.executors.submit(
169                     new ValidatorCallable(validator, env, files)
170                 )
171             );
172         }
173         return futures;
174     }
175 
176     /**
177      * Filter files based on excludes.
178      * @param env Maven environment
179      * @param files Files to exclude
180      * @param validator Validator to use
181      * @return Filtered files
182      */
183     private static Collection<File> filter(final MavenEnvironment env,
184         final Collection<File> files, final ResourceValidator validator) {
185         final Collection<File> filtered = new LinkedList<>();
186         for (final File file : files) {
187             if (
188                 !env.exclude(
189                     validator.name().toLowerCase(Locale.ENGLISH),
190                     file.toString()
191                 )
192             ) {
193                 filtered.add(file);
194             }
195         }
196         return filtered;
197     }
198 
199     /**
200      * Callable for validators.
201      *
202      * @since 0.1
203      */
204     private static class ValidatorCallable
205         implements Callable<Collection<Violation>> {
206         /**
207          * Validator to use.
208          */
209         private final ResourceValidator validator;
210 
211         /**
212          * Maven environment.
213          */
214         private final MavenEnvironment env;
215 
216         /**
217          * List of files to validate.
218          */
219         private final Collection<File> files;
220 
221         /**
222          * Constructor.
223          * @param validator Validator to use
224          * @param env Maven environment
225          * @param files List of files to validate
226          */
227         ValidatorCallable(final ResourceValidator validator,
228             final MavenEnvironment env, final Collection<File> files) {
229             this.validator = validator;
230             this.env = env;
231             this.files = files;
232         }
233 
234         @Override
235         public Collection<Violation> call() {
236             return this.validator.validate(
237                 CheckMojo.filter(this.env, this.files, this.validator)
238             );
239         }
240     }
241 }