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