View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.enforcer.rules;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.nio.file.Files;
28  import java.util.Objects;
29  
30  import org.apache.maven.enforcer.rule.api.AbstractEnforcerRuleConfigProvider;
31  import org.apache.maven.enforcer.rule.api.EnforcerRuleError;
32  import org.apache.maven.enforcer.rules.utils.ExpressionEvaluator;
33  import org.apache.maven.plugin.MojoExecution;
34  import org.codehaus.plexus.util.xml.Xpp3Dom;
35  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
36  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
37  
38  /**
39   * An enforcer rule that will provide rules configuration from an external resource.
40   *
41   * @author <a href="mailto:gastaldi@apache.org">George Gastaldi</a>
42   * @since 3.2.0
43   */
44  @Named("externalRules")
45  public final class ExternalRules extends AbstractEnforcerRuleConfigProvider {
46      private static final String LOCATION_PREFIX_CLASSPATH = "classpath:";
47  
48      /**
49       * The external rules location. If it starts with "classpath:", the resource is read from the classpath.
50       * Otherwise, it is handled as a filesystem path, either absolute, or relative to <code>${project.basedir}</code>
51       */
52      private String location;
53  
54      private final MojoExecution mojoExecution;
55  
56      private final ExpressionEvaluator evaluator;
57  
58      @Inject
59      public ExternalRules(MojoExecution mojoExecution, ExpressionEvaluator evaluator) {
60          this.mojoExecution = Objects.requireNonNull(mojoExecution);
61          this.evaluator = Objects.requireNonNull(evaluator);
62      }
63  
64      public void setLocation(String location) {
65          this.location = location;
66      }
67  
68      @Override
69      public Xpp3Dom getRulesConfig() throws EnforcerRuleError {
70  
71          try (InputStream descriptorStream = resolveDescriptor()) {
72              Xpp3Dom enforcerRules = Xpp3DomBuilder.build(descriptorStream, "UTF-8");
73              if (enforcerRules.getChildCount() == 1 && "enforcer".equals(enforcerRules.getName())) {
74                  return enforcerRules.getChild(0);
75              } else {
76                  throw new EnforcerRuleError("Enforcer rules configuration not found in: " + location);
77              }
78          } catch (IOException | XmlPullParserException e) {
79              throw new EnforcerRuleError(e);
80          }
81      }
82  
83      private InputStream resolveDescriptor() throws EnforcerRuleError {
84          InputStream descriptorStream;
85          if (location != null) {
86              if (location.startsWith(LOCATION_PREFIX_CLASSPATH)) {
87                  String classpathLocation = location.substring(LOCATION_PREFIX_CLASSPATH.length());
88                  getLog().debug("Read rules form classpath location: " + classpathLocation);
89                  ClassLoader classRealm = mojoExecution.getMojoDescriptor().getRealm();
90                  descriptorStream = classRealm.getResourceAsStream(classpathLocation);
91                  if (descriptorStream == null) {
92                      throw new EnforcerRuleError("Location '" + classpathLocation + "' not found in classpath");
93                  }
94              } else {
95                  File descriptorFile = evaluator.alignToBaseDirectory(new File(location));
96                  getLog().debug("Read rules form file location: " + descriptorFile);
97                  try {
98                      descriptorStream = Files.newInputStream(descriptorFile.toPath());
99                  } catch (IOException e) {
100                     throw new EnforcerRuleError("Could not read descriptor in " + descriptorFile, e);
101                 }
102             }
103         } else {
104             throw new EnforcerRuleError("No location provided");
105         }
106         return descriptorStream;
107     }
108 
109     @Override
110     public String toString() {
111         return String.format("ExternalRules[location=%s]", location);
112     }
113 }