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    }