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.configuration.internal;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.io.File;
25  import java.nio.file.Path;
26  import java.nio.file.Paths;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.Objects;
30  
31  import org.apache.maven.api.xml.XmlNode;
32  import org.apache.maven.configuration.BeanConfigurationException;
33  import org.apache.maven.configuration.BeanConfigurationPathTranslator;
34  import org.apache.maven.configuration.BeanConfigurationRequest;
35  import org.apache.maven.configuration.BeanConfigurationValuePreprocessor;
36  import org.apache.maven.configuration.BeanConfigurator;
37  import org.apache.maven.internal.xml.XmlNodeImpl;
38  import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
39  import org.codehaus.plexus.component.configurator.ConfigurationListener;
40  import org.codehaus.plexus.component.configurator.converters.AbstractConfigurationConverter;
41  import org.codehaus.plexus.component.configurator.converters.basic.AbstractBasicConverter;
42  import org.codehaus.plexus.component.configurator.converters.composite.ObjectWithFieldsConverter;
43  import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
44  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
45  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
46  import org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator;
47  import org.codehaus.plexus.configuration.PlexusConfiguration;
48  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
49  import org.codehaus.plexus.util.xml.Xpp3Dom;
50  
51  /**
52   * <strong>Warning:</strong> This is an internal class that is only public for technical reasons, it is not part of the
53   * public API. In particular, this class can be changed or deleted without prior notice.
54   *
55   */
56  @Named
57  @Singleton
58  public class DefaultBeanConfigurator implements BeanConfigurator {
59  
60      private final ConverterLookup converterLookup;
61  
62      public DefaultBeanConfigurator() {
63          converterLookup = new EnhancedConverterLookup();
64      }
65  
66      public void configureBean(BeanConfigurationRequest request) throws BeanConfigurationException {
67          Objects.requireNonNull(request, "request cannot be null");
68          Objects.requireNonNull(request.getBean(), "request.bean cannot be null");
69  
70          Object configuration = request.getConfiguration();
71          if (configuration == null) {
72              return;
73          }
74  
75          PlexusConfiguration plexusConfig;
76          if (configuration instanceof PlexusConfiguration) {
77              plexusConfig = (PlexusConfiguration) configuration;
78          } else if (configuration instanceof Xpp3Dom) {
79              plexusConfig = new XmlPlexusConfiguration((Xpp3Dom) configuration);
80          } else {
81              throw new BeanConfigurationException("unsupported bean configuration source ("
82                      + configuration.getClass().getName() + ")");
83          }
84  
85          if (request.getConfigurationElement() != null) {
86              plexusConfig = plexusConfig.getChild(request.getConfigurationElement());
87          }
88  
89          ClassLoader classLoader = request.getClassLoader();
90          if (classLoader == null) {
91              classLoader = request.getBean().getClass().getClassLoader();
92          }
93  
94          BeanExpressionEvaluator evaluator = new BeanExpressionEvaluator(request);
95  
96          ObjectWithFieldsConverter converter = new EnhancedConfigurationConverter();
97  
98          try {
99              converter.processConfiguration(
100                     converterLookup, request.getBean(), classLoader, plexusConfig, evaluator, null);
101         } catch (ComponentConfigurationException e) {
102             throw new BeanConfigurationException(e.getMessage(), e);
103         }
104     }
105 
106     static class BeanExpressionEvaluator implements TypeAwareExpressionEvaluator {
107 
108         private final BeanConfigurationValuePreprocessor preprocessor;
109 
110         private final BeanConfigurationPathTranslator translator;
111 
112         BeanExpressionEvaluator(BeanConfigurationRequest request) {
113             preprocessor = request.getValuePreprocessor();
114             translator = request.getPathTranslator();
115         }
116 
117         public Object evaluate(String expression, Class<?> type) throws ExpressionEvaluationException {
118             if (preprocessor != null) {
119                 try {
120                     return preprocessor.preprocessValue(expression, type);
121                 } catch (BeanConfigurationException e) {
122                     throw new ExpressionEvaluationException(e.getMessage(), e);
123                 }
124             }
125             return expression;
126         }
127 
128         public Object evaluate(String expression) throws ExpressionEvaluationException {
129             return evaluate(expression, null);
130         }
131 
132         public File alignToBaseDirectory(File file) {
133             if (translator != null) {
134                 return translator.translatePath(file);
135             }
136             return file;
137         }
138     }
139 
140     static class XmlConverter extends AbstractConfigurationConverter {
141         @Override
142         public boolean canConvert(Class<?> type) {
143             return XmlNode.class.equals(type);
144         }
145 
146         @Override
147         public Object fromConfiguration(
148                 final ConverterLookup lookup,
149                 final PlexusConfiguration configuration,
150                 final Class<?> type,
151                 final Class<?> enclosingType,
152                 final ClassLoader loader,
153                 final ExpressionEvaluator evaluator,
154                 final ConfigurationListener listener)
155                 throws ComponentConfigurationException {
156 
157             try {
158                 return toXml(configuration, evaluator);
159             } catch (ExpressionEvaluationException e) {
160                 throw new ComponentConfigurationException("Unable to convert configuration to xml node", e);
161             }
162         }
163 
164         XmlNode toXml(PlexusConfiguration config, ExpressionEvaluator evaluator) throws ExpressionEvaluationException {
165             List<XmlNode> children = new ArrayList<>();
166             for (PlexusConfiguration c : config.getChildren()) {
167                 children.add(toXml(c, evaluator));
168             }
169             String name = config.getName();
170             Object value = evaluator.evaluate(config.getValue());
171             return new XmlNodeImpl(name, value != null ? value.toString() : null, null, children, null);
172         }
173     }
174 
175     static class PathConverter extends AbstractBasicConverter {
176         @Override
177         public boolean canConvert(Class<?> type) {
178             return Path.class.equals(type);
179         }
180 
181         @Override
182         protected Object fromString(String value) throws ComponentConfigurationException {
183             return Paths.get(value.replace('/' == File.separatorChar ? '\\' : '/', File.separatorChar));
184         }
185 
186         @Override
187         public Object fromConfiguration(
188                 final ConverterLookup lookup,
189                 final PlexusConfiguration configuration,
190                 final Class<?> type,
191                 final Class<?> enclosingType,
192                 final ClassLoader loader,
193                 final ExpressionEvaluator evaluator,
194                 final ConfigurationListener listener)
195                 throws ComponentConfigurationException {
196             final Object result =
197                     super.fromConfiguration(lookup, configuration, type, enclosingType, loader, evaluator, listener);
198 
199             return result instanceof Path
200                     ? evaluator.alignToBaseDirectory(((Path) result).toFile()).toPath()
201                     : result;
202         }
203     }
204 }