View Javadoc

1   /**
2    * Copyright 2011-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.maven.plugins.spring;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  import java.util.Properties;
22  
23  import org.apache.maven.plugin.AbstractMojo;
24  import org.apache.maven.plugin.MojoExecutionException;
25  import org.apache.maven.project.MavenProject;
26  import org.kuali.common.util.PropertyUtils;
27  import org.kuali.common.util.service.SpringService;
28  import org.springframework.util.Assert;
29  
30  /**
31   * <p>
32   * This mojo provides the ability to load a Spring context XML file. It uses a lightweight integration technique between Spring and Maven
33   * centered around <code>java.util.Properties</code>. Prior to the Spring context being loaded, it is injected with a
34   * <code>java.util.Properties</code> object containing the full set of Maven properties. The <code>java.util.Properties</code> object is
35   * registered in the context as a bean under the name <code>mavenProperties</code>.
36   * </p>
37   * <p>
38   * One common use of the injected Maven properties in a Spring context is for replacing property placeholders.
39   * </p>
40   * <p>
41   * For example:
42   * </p>
43   *
44   * <pre>
45   *  &lt;beans&gt;
46   *   &lt;context:property-placeholder properties-ref="mavenProperties" /&gt;
47   *   &lt;bean id="artifactId" class="java.lang.String"&gt;
48   *    &lt;constructor-arg value="${project.artifactId}" /&gt;
49   *   &lt;/bean&gt;
50   *  &lt;/beans&gt;
51   * </pre>
52   *
53   * @goal load
54   */
55  public class LoadMojo extends AbstractMojo {
56  
57  	/**
58  	 * Maven project
59  	 *
60  	 * @parameter default-value="${project}"
61  	 * @required
62  	 * @readonly
63  	 */
64  	private MavenProject project;
65  
66  	/**
67  	 * Location of a Spring context XML file. This can be a file on the local file system, or any URL Spring's Resource loading framework
68  	 * understands eg {@code classpath:my-context.xml}
69  	 *
70  	 * @parameter expression="${spring.location}" default-value="classpath:${project.artifactId}-context.xml"
71  	 * @required
72  	 */
73  	private String location;
74  
75  	/**
76  	 * List of additional Spring context XML files to load (if any).
77  	 *
78  	 * @parameter
79  	 */
80  	private List<String> locations;
81  
82  	/**
83  	 * Additional properties to supply to the Spring context.
84  	 *
85  	 * @parameter
86  	 */
87  	private Properties properties;
88  
89  	/**
90  	 * If true, Maven properties are injected into the Spring context as a <code>java.util.Properties</code> object
91  	 *
92  	 * @parameter expression="${spring.injectProperties}" default-value="true"
93  	 */
94  	private boolean injectProperties;
95  
96  	/**
97  	 * If true, the <code>MavenProject</code> object is injected into the Spring context
98  	 *
99  	 * @parameter expression="${spring.injectProject}" default-value="false"
100 	 */
101 	private boolean injectProject;
102 
103 	/**
104 	 * The name to use when registering the <code>java.util.Properties</code> object containing Maven properties as a bean in the Spring
105 	 * context.
106 	 *
107 	 * @parameter expression="${spring.propertiesBeanName}" default-value="mavenProperties"
108 	 */
109 	private String propertiesBeanName;
110 
111 	/**
112 	 * The name to use when registering the <code>MavenProject</code> object as a bean in the Spring context.
113 	 *
114 	 * @parameter expression="${spring.projectBeanName}" default-value="project"
115 	 */
116 	private String projectBeanName;
117 
118 	/**
119 	 * The implementation of {@code org.kuali.common.util.service.SpringService} to use
120 	 *
121 	 * @parameter expression="${spring.serviceClassname}" default-value="org.kuali.common.util.service.DefaultSpringService"
122 	 * @required
123 	 */
124 	private String serviceClassname;
125 
126 	@Override
127 	public void execute() throws MojoExecutionException {
128 
129 		// The ordering here is significant.
130 		// Properties supplied directly to the mojo override properties from project.getProperties()
131 		// But, internal Maven properties need to always win.
132 		// ${project.artifactId} needs to always faithfully represent the correct artifactId
133 		this.properties = PropertyUtils.combine(project.getProperties(), properties, MavenUtils.getInternalProperties(project));
134 
135 		// Combine the list with the single value
136 		this.locations = combine(location, locations);
137 
138 		// Log what we are up to
139 		logConfiguration();
140 
141 		// Invoke the service to load the context
142 		SpringService service = getService(serviceClassname);
143 		service.load(locations, getBeanNames(), getBeans());
144 	}
145 
146 	protected List<Object> getBeans() {
147 		List<Object> beans = new ArrayList<Object>();
148 		if (injectProperties) {
149 			beans.add(properties);
150 		}
151 		if (injectProject) {
152 			beans.add(project);
153 		}
154 		return beans;
155 	}
156 
157 	protected List<String> getBeanNames() {
158 		List<String> beanNames = new ArrayList<String>();
159 		if (injectProperties) {
160 			beanNames.add(propertiesBeanName);
161 		}
162 		if (injectProject) {
163 			beanNames.add(projectBeanName);
164 		}
165 		return beanNames;
166 	}
167 
168 	protected void logConfiguration() {
169 		if (injectProperties) {
170 			getLog().info("Injecting " + properties.size() + " Maven properties as a [" + properties.getClass().getName() + "] bean under the id [" + propertiesBeanName + "]");
171 			getLog().debug("Displaying " + properties.size() + " properties\n\n" + PropertyUtils.toString(properties));
172 		}
173 		if (injectProject) {
174 			getLog().info("Injecting the Maven project as a [" + project.getClass().getName() + "] bean under the id [" + projectBeanName + "]");
175 		}
176 		if (locations.size() > 1) {
177 			getLog().info("Loading " + locations.size() + " Spring context files");
178 		}
179 	}
180 
181 	protected SpringService getService(String serviceClassname) {
182 		try {
183 			Class<?> serviceClass = Class.forName(serviceClassname);
184 			return (SpringService) serviceClass.newInstance();
185 		} catch (ClassNotFoundException e) {
186 			throw new IllegalStateException("Unexpected error", e);
187 		} catch (IllegalAccessException e) {
188 			throw new IllegalStateException("Unexpected error", e);
189 		} catch (InstantiationException e) {
190 			throw new IllegalStateException("Unexpected error", e);
191 		}
192 	}
193 
194 	/**
195 	 * Return a combined list where <code>string</code> is always the first element and the Strings in the list are unique
196 	 */
197 	protected List<String> combine(String required, List<String> optional) {
198 		Assert.notNull(required);
199 		if (optional == null) {
200 			return Collections.singletonList(required);
201 		} else {
202 			List<String> combined = new ArrayList<String>(optional);
203 			// Always insert required as the first element in the list
204 			combined.add(required);
205 			// Add the other strings but ensure the list does not already contain them
206 			for (String element : optional) {
207 				boolean doesNotContain = !combined.contains(element);
208 				Assert.isTrue(doesNotContain);
209 				combined.add(element);
210 			}
211 			return combined;
212 		}
213 	}
214 
215 	public String getLocation() {
216 		return location;
217 	}
218 
219 	public void setLocation(String location) {
220 		this.location = location;
221 	}
222 
223 	public List<String> getLocations() {
224 		return locations;
225 	}
226 
227 	public void setLocations(List<String> locations) {
228 		this.locations = locations;
229 	}
230 
231 	public Properties getProperties() {
232 		return properties;
233 	}
234 
235 	public void setProperties(Properties properties) {
236 		this.properties = properties;
237 	}
238 
239 	public String getPropertiesBeanName() {
240 		return propertiesBeanName;
241 	}
242 
243 	public void setPropertiesBeanName(String propertiesBeanName) {
244 		this.propertiesBeanName = propertiesBeanName;
245 	}
246 
247 	public String getServiceClassname() {
248 		return serviceClassname;
249 	}
250 
251 	public void setServiceClassname(String serviceClassname) {
252 		this.serviceClassname = serviceClassname;
253 	}
254 
255 	public MavenProject getProject() {
256 		return project;
257 	}
258 
259 	public boolean isInjectProperties() {
260 		return injectProperties;
261 	}
262 
263 	public void setInjectProperties(boolean injectProperties) {
264 		this.injectProperties = injectProperties;
265 	}
266 
267 	public boolean isInjectProject() {
268 		return injectProject;
269 	}
270 
271 	public void setInjectProject(boolean injectProject) {
272 		this.injectProject = injectProject;
273 	}
274 
275 	public String getProjectBeanName() {
276 		return projectBeanName;
277 	}
278 
279 	public void setProjectBeanName(String projectBeanName) {
280 		this.projectBeanName = projectBeanName;
281 	}
282 
283 }