View Javadoc

1   /**
2    * Copyright 2010-2013 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.common.util.service;
17  
18  import java.io.File;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.List;
23  
24  import org.kuali.common.util.Assert;
25  import org.kuali.common.util.CollectionUtils;
26  import org.kuali.common.util.LocationUtils;
27  import org.kuali.common.util.spring.SpringUtils;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  import org.springframework.context.ConfigurableApplicationContext;
31  import org.springframework.context.annotation.AnnotationConfigApplicationContext;
32  import org.springframework.context.support.ClassPathXmlApplicationContext;
33  import org.springframework.core.env.ConfigurableEnvironment;
34  import org.springframework.core.env.MutablePropertySources;
35  import org.springframework.core.env.PropertySource;
36  
37  public class DefaultSpringService implements SpringService {
38  
39  	private static final Logger logger = LoggerFactory.getLogger(DefaultSpringService.class);
40  
41  	@Override
42  	public void load(Class<?> annotatedClass) {
43  		load(annotatedClass, null, null);
44  	}
45  
46  	@Override
47  	public void load(Class<?> annotatedClass, String beanName, Object bean, PropertySource<?> propertySource) {
48  		// Make sure the annotatedClass isn't null
49  		Assert.notNull(annotatedClass, "annotatedClass is null");
50  
51  		// Setup a SpringContext
52  		SpringContext context = new SpringContext();
53  		context.setAnnotatedClasses(CollectionUtils.asList(annotatedClass));
54  		context.setPropertySourceContext(new PropertySourceContext(SpringUtils.asList(propertySource)));
55  
56  		// Null safe handling for non-required parameters
57  		context.setBeanNames(CollectionUtils.toEmptyList(beanName));
58  		context.setBeans(CollectionUtils.toEmptyList(bean));
59  
60  		// Load the configuration from the annotated class
61  		load(context);
62  	}
63  
64  	@Override
65  	public void load(Class<?> annotatedClass, String beanName, Object bean) {
66  		load(annotatedClass, beanName, bean, null);
67  	}
68  
69  	@Override
70  	public void load(String location) {
71  		load(location, null, null);
72  	}
73  
74  	@Override
75  	public void load(String location, String beanName, Object bean, PropertySource<?> propertySource) {
76  		// Make sure the location isn't empty
77  		Assert.hasText(location, "location is null");
78  
79  		// Setup a SpringContext
80  		SpringContext context = new SpringContext();
81  		context.setLocations(Arrays.asList(location));
82  		context.setPropertySourceContext(new PropertySourceContext(SpringUtils.asList(propertySource)));
83  
84  		// Null safe handling for non-required parameters
85  		context.setBeanNames(CollectionUtils.toEmptyList(beanName));
86  		context.setBeans(CollectionUtils.toEmptyList(bean));
87  
88  		// Load the location using a SpringContext
89  		load(context);
90  	}
91  
92  	@Override
93  	public void load(String location, String beanName, Object bean) {
94  		load(location, beanName, bean, null);
95  	}
96  
97  	@Override
98  	public void load(SpringContext context) {
99  
100 		// Null-safe handling for parameters
101 		context.setBeanNames(CollectionUtils.toEmptyList(context.getBeanNames()));
102 		context.setBeans(CollectionUtils.toEmptyList(context.getBeans()));
103 		context.setAnnotatedClasses(CollectionUtils.toEmptyList(context.getAnnotatedClasses()));
104 		context.setLocations(CollectionUtils.toEmptyList(context.getLocations()));
105 
106 		// Make sure we have at least one location or annotated class
107 		boolean empty = CollectionUtils.isEmpty(context.getLocations()) && CollectionUtils.isEmpty(context.getAnnotatedClasses());
108 		Assert.isFalse(empty, "Both locations and annotatedClasses are empty");
109 
110 		// Make sure we have a name for every bean
111 		Assert.isTrue(context.getBeanNames().size() == context.getBeans().size());
112 
113 		// Make sure all of the locations exist
114 		SpringUtils.validateExists(context.getLocations());
115 
116 		// Convert any file names to fully qualified file system URL's
117 		List<String> convertedLocations = getConvertedLocations(context.getLocations());
118 
119 		// The Spring classes prefer array's
120 		String[] locationsArray = CollectionUtils.toStringArray(convertedLocations);
121 
122 		ConfigurableApplicationContext parent = null;
123 		ConfigurableApplicationContext xmlChild = null;
124 		AnnotationConfigApplicationContext annotationChild = null;
125 		try {
126 			if (isParentContextRequired(context)) {
127 				// Construct a parent context if necessary
128 				parent = SpringUtils.getContextWithPreRegisteredBeans(context.getBeanNames(), context.getBeans());
129 			}
130 
131 			if (!CollectionUtils.isEmpty(context.getAnnotatedClasses())) {
132 				// Create an annotation based application context wrapped in a parent context
133 				annotationChild = getAnnotationContext(context, parent);
134 				// Add custom property sources (if any)
135 				addPropertySources(context, annotationChild);
136 
137 			}
138 
139 			if (!CollectionUtils.isEmpty(context.getLocations())) {
140 				// Create an XML application context wrapped in a parent context
141 				xmlChild = new ClassPathXmlApplicationContext(locationsArray, false, parent);
142 				// Add custom property sources (if any)
143 				addPropertySources(context, xmlChild);
144 			}
145 
146 			// Invoke refresh to load the context
147 			SpringUtils.refreshQuietly(annotationChild);
148 			SpringUtils.refreshQuietly(xmlChild);
149 		} finally {
150 			// cleanup
151 			// closeQuietly(annotationChild);
152 			// closeQuietly(xmlChild);
153 			// closeQuietly(parent);
154 		}
155 	}
156 
157 	protected AnnotationConfigApplicationContext getAnnotationContext(SpringContext context, ConfigurableApplicationContext parent) {
158 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
159 		if (parent != null) {
160 			ctx.setParent(parent);
161 		}
162 		for (Class<?> annotatedClass : context.getAnnotatedClasses()) {
163 			ctx.register(annotatedClass);
164 		}
165 		return ctx;
166 	}
167 
168 	protected void addPropertySources(SpringContext context, ConfigurableApplicationContext applicationContext) {
169 		PropertySourceContext psc = context.getPropertySourceContext();
170 		ConfigurableEnvironment env = applicationContext.getEnvironment();
171 		if (psc.isRemoveExistingSources()) {
172 			logger.debug("Removing all existing property sources");
173 			SpringUtils.removeAllPropertySources(env);
174 		}
175 
176 		if (CollectionUtils.isEmpty(psc.getSources())) {
177 			return;
178 		}
179 		List<PropertySource<?>> propertySources = psc.getSources();
180 		MutablePropertySources sources = env.getPropertySources();
181 		if (psc.isLastOneInWins()) {
182 			Collections.reverse(propertySources);
183 		}
184 		PropertySourceAddPriority priority = psc.getPriority();
185 		for (PropertySource<?> propertySource : propertySources) {
186 			Object[] args = { propertySource.getName(), propertySource.getClass().getName(), priority };
187 			logger.debug("Adding property source - [{}] -> [{}] Priority=[{}]", args);
188 			switch (priority) {
189 			case FIRST:
190 				sources.addFirst(propertySource);
191 				break;
192 			case LAST:
193 				sources.addLast(propertySource);
194 				break;
195 			default:
196 				throw new IllegalStateException(priority + " is an unknown priority");
197 			}
198 		}
199 	}
200 
201 	/**
202 	 * Return true if the context contains any beans or beanNames, false otherwise.
203 	 */
204 	protected boolean isParentContextRequired(SpringContext context) {
205 		if (!CollectionUtils.isEmpty(context.getBeanNames())) {
206 			return true;
207 		} else if (!CollectionUtils.isEmpty(context.getBeans())) {
208 			return true;
209 		} else {
210 			return false;
211 		}
212 	}
213 
214 	/**
215 	 * Convert any locations representing an existing file into a fully qualified file system url. Leave any locations that do not resolve to an existing file alone.
216 	 */
217 	protected List<String> getConvertedLocations(List<String> locations) {
218 		List<String> converted = new ArrayList<String>();
219 		for (String location : locations) {
220 			if (LocationUtils.isExistingFile(location)) {
221 				File file = new File(location);
222 				// ClassPathXmlApplicationContext needs a fully qualified URL, not a filename
223 				String url = LocationUtils.getCanonicalURLString(file);
224 				converted.add(url);
225 			} else {
226 				converted.add(location);
227 			}
228 		}
229 		return converted;
230 	}
231 
232 }