001 /** 002 * Copyright 2010-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.common.util.service; 017 018 import java.io.File; 019 import java.util.ArrayList; 020 import java.util.Arrays; 021 import java.util.Collections; 022 import java.util.List; 023 024 import org.apache.commons.lang3.StringUtils; 025 import org.kuali.common.util.Assert; 026 import org.kuali.common.util.CollectionUtils; 027 import org.kuali.common.util.LocationUtils; 028 import org.kuali.common.util.spring.SpringUtils; 029 import org.slf4j.Logger; 030 import org.slf4j.LoggerFactory; 031 import org.springframework.context.ApplicationContext; 032 import org.springframework.context.ConfigurableApplicationContext; 033 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 034 import org.springframework.context.support.ClassPathXmlApplicationContext; 035 import org.springframework.core.env.ConfigurableEnvironment; 036 import org.springframework.core.env.MutablePropertySources; 037 import org.springframework.core.env.PropertySource; 038 039 @Deprecated 040 public class DefaultSpringService implements SpringService { 041 042 private static final Logger logger = LoggerFactory.getLogger(DefaultSpringService.class); 043 044 @Override 045 public void load(Class<?> annotatedClass) { 046 load(annotatedClass, null, null); 047 } 048 049 @Override 050 public void load(Class<?> annotatedClass, String beanName, Object bean, PropertySource<?> propertySource) { 051 // Make sure the annotatedClass isn't null 052 Assert.notNull(annotatedClass, "annotatedClass is null"); 053 054 // Setup a SpringContext 055 SpringContext context = new SpringContext(); 056 context.setAnnotatedClasses(CollectionUtils.asList(annotatedClass)); 057 context.setPropertySourceContext(new PropertySourceContext(SpringUtils.asList(propertySource))); 058 059 // Null safe handling for non-required parameters 060 context.setBeanNames(CollectionUtils.toEmptyList(beanName)); 061 context.setBeans(CollectionUtils.toEmptyList(bean)); 062 063 // Load the configuration from the annotated class 064 load(context); 065 } 066 067 @Override 068 public void load(Class<?> annotatedClass, String beanName, Object bean) { 069 load(annotatedClass, beanName, bean, null); 070 } 071 072 @Override 073 public void load(String location) { 074 load(location, null, null); 075 } 076 077 @Override 078 public void load(String location, String beanName, Object bean, PropertySource<?> propertySource) { 079 // Make sure the location isn't empty 080 Assert.hasText(location, "location is null"); 081 082 // Setup a SpringContext 083 SpringContext context = new SpringContext(); 084 context.setLocations(Arrays.asList(location)); 085 context.setPropertySourceContext(new PropertySourceContext(SpringUtils.asList(propertySource))); 086 087 // Null safe handling for non-required parameters 088 context.setBeanNames(CollectionUtils.toEmptyList(beanName)); 089 context.setBeans(CollectionUtils.toEmptyList(bean)); 090 091 // Load the location using a SpringContext 092 load(context); 093 } 094 095 @Override 096 public void load(String location, String beanName, Object bean) { 097 load(location, beanName, bean, null); 098 } 099 100 @Override 101 public void load(SpringContext context) { 102 103 // Null-safe handling for parameters 104 context.setBeanNames(CollectionUtils.toEmptyList(context.getBeanNames())); 105 context.setBeans(CollectionUtils.toEmptyList(context.getBeans())); 106 context.setAnnotatedClasses(CollectionUtils.toEmptyList(context.getAnnotatedClasses())); 107 context.setLocations(CollectionUtils.toEmptyList(context.getLocations())); 108 109 // Make sure we have at least one location or annotated class 110 boolean empty = CollectionUtils.isEmpty(context.getLocations()) && CollectionUtils.isEmpty(context.getAnnotatedClasses()); 111 Assert.isFalse(empty, "Both locations and annotatedClasses are empty"); 112 113 // Make sure we have a name for every bean 114 Assert.isTrue(context.getBeanNames().size() == context.getBeans().size()); 115 116 // Make sure all of the locations exist 117 SpringUtils.validateExists(context.getLocations()); 118 119 // Convert any file names to fully qualified file system URL's 120 List<String> convertedLocations = getConvertedLocations(context.getLocations()); 121 122 // The Spring classes prefer array's 123 String[] locationsArray = CollectionUtils.toStringArray(convertedLocations); 124 125 ConfigurableApplicationContext parent = null; 126 ClassPathXmlApplicationContext xmlChild = null; 127 AnnotationConfigApplicationContext annotationChild = null; 128 try { 129 if (isParentContextRequired(context)) { 130 // Construct a parent context if necessary 131 parent = SpringUtils.getContextWithPreRegisteredBeans(context.getId(), context.getDisplayName(), context.getBeanNames(), context.getBeans()); 132 } 133 134 if (!CollectionUtils.isEmpty(context.getAnnotatedClasses())) { 135 // Create an annotation based application context wrapped in a parent context 136 annotationChild = getAnnotationContext(context, parent); 137 // Add custom property sources (if any) 138 addPropertySources(context, annotationChild); 139 140 } 141 142 if (!CollectionUtils.isEmpty(context.getLocations())) { 143 // Create an XML application context wrapped in a parent context 144 xmlChild = new ClassPathXmlApplicationContext(locationsArray, false, parent); 145 if (parent == null) { 146 addMetaInfo(xmlChild, context); 147 } 148 // Add custom property sources (if any) 149 addPropertySources(context, xmlChild); 150 } 151 152 // Invoke refresh to load the context 153 SpringUtils.refreshQuietly(annotationChild); 154 SpringUtils.refreshQuietly(xmlChild); 155 debugQuietly(parent, annotationChild, xmlChild); 156 } finally { 157 // cleanup 158 // closeQuietly(annotationChild); 159 // closeQuietly(xmlChild); 160 // closeQuietly(parent); 161 } 162 } 163 164 protected void debugQuietly(ApplicationContext parent, ApplicationContext child1, ApplicationContext child2) { 165 if (!logger.isDebugEnabled()) { 166 return; 167 } 168 if (parent != null) { 169 SpringUtils.debug(parent); 170 } else { 171 if (child1 != null) { 172 SpringUtils.debug(child1); 173 } 174 if (child2 != null) { 175 SpringUtils.debug(child2); 176 } 177 } 178 } 179 180 /** 181 * Add id and display name to the ApplicationContext if they are not blank 182 */ 183 protected void addMetaInfo(AnnotationConfigApplicationContext ctx, SpringContext sc) { 184 if (!StringUtils.isBlank(sc.getId())) { 185 ctx.setId(sc.getId()); 186 } 187 if (!StringUtils.isBlank(sc.getDisplayName())) { 188 ctx.setDisplayName(sc.getDisplayName()); 189 } 190 } 191 192 /** 193 * Add id and display name to the ApplicationContext if they are not blank 194 */ 195 protected void addMetaInfo(ClassPathXmlApplicationContext ctx, SpringContext sc) { 196 if (!StringUtils.isBlank(sc.getId())) { 197 ctx.setId(sc.getId()); 198 } 199 if (!StringUtils.isBlank(sc.getDisplayName())) { 200 ctx.setDisplayName(sc.getDisplayName()); 201 } 202 } 203 204 protected AnnotationConfigApplicationContext getAnnotationContext(SpringContext context, ConfigurableApplicationContext parent) { 205 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); 206 if (parent != null) { 207 ctx.setParent(parent); 208 } else { 209 addMetaInfo(ctx, context); 210 } 211 for (Class<?> annotatedClass : context.getAnnotatedClasses()) { 212 ctx.register(annotatedClass); 213 } 214 return ctx; 215 } 216 217 protected void addPropertySources(SpringContext context, ConfigurableApplicationContext applicationContext) { 218 PropertySourceContext psc = context.getPropertySourceContext(); 219 ConfigurableEnvironment env = applicationContext.getEnvironment(); 220 if (psc.isRemoveExistingSources()) { 221 logger.debug("Removing all existing property sources"); 222 SpringUtils.removeAllPropertySources(env); 223 } 224 225 if (CollectionUtils.isEmpty(psc.getSources())) { 226 return; 227 } 228 List<PropertySource<?>> propertySources = psc.getSources(); 229 MutablePropertySources sources = env.getPropertySources(); 230 if (psc.isLastOneInWins()) { 231 Collections.reverse(propertySources); 232 } 233 PropertySourceAddPriority priority = psc.getPriority(); 234 for (PropertySource<?> propertySource : propertySources) { 235 Object[] args = { propertySource.getName(), propertySource.getClass().getName(), priority }; 236 logger.debug("Adding property source - [{}] -> [{}] Priority=[{}]", args); 237 switch (priority) { 238 case FIRST: 239 sources.addFirst(propertySource); 240 break; 241 case LAST: 242 sources.addLast(propertySource); 243 break; 244 default: 245 throw new IllegalStateException(priority + " is an unknown priority"); 246 } 247 } 248 } 249 250 /** 251 * Return true if the context contains any beans or beanNames, false otherwise. 252 */ 253 protected boolean isParentContextRequired(SpringContext context) { 254 if (!CollectionUtils.isEmpty(context.getBeanNames())) { 255 return true; 256 } else if (!CollectionUtils.isEmpty(context.getBeans())) { 257 return true; 258 } else { 259 return false; 260 } 261 } 262 263 /** 264 * 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. 265 */ 266 protected List<String> getConvertedLocations(List<String> locations) { 267 List<String> converted = new ArrayList<String>(); 268 for (String location : locations) { 269 if (LocationUtils.isExistingFile(location)) { 270 File file = new File(location); 271 // ClassPathXmlApplicationContext needs a fully qualified URL, not a filename 272 String url = LocationUtils.getCanonicalURLString(file); 273 converted.add(url); 274 } else { 275 converted.add(location); 276 } 277 } 278 return converted; 279 } 280 281 }