001 /* 002 * Copyright 2002-2006 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 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, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016 package org.springframework.richclient.application; 017 018 import org.apache.commons.logging.Log; 019 import org.apache.commons.logging.LogFactory; 020 import org.springframework.beans.BeansException; 021 import org.springframework.beans.factory.config.BeanDefinition; 022 import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 023 import org.springframework.beans.factory.config.BeanPostProcessor; 024 import org.springframework.beans.factory.config.ConfigurableBeanFactory; 025 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 026 import org.springframework.context.MessageSource; 027 import org.springframework.richclient.progress.ProgressMonitor; 028 import org.springframework.util.Assert; 029 030 /** 031 * A {@code BeanFactoryPostProcessor} that notifies a specified 032 * {@link ProgressMonitor} of progress made while loading a bean factory. 033 * 034 * <p> 035 * The messages sent to the progress monitor can be internationalized by 036 * providing a {@link MessageSource} to the constructor of this class. Note that 037 * if a {@link MessageSource} is provided it must already be initialized in 038 * order for it to successfully retrieve messages. 039 * </p> 040 * 041 * <p> 042 * The progress monitor will be notified once prior to initializing the beans in 043 * the bean factory and once for each singleton bean before it is initialized. 044 * The message keys used to find these messages are 045 * {@value #LOADING_APP_CONTEXT_KEY} and {@value #LOADING_BEAN_KEY}. If the 046 * message source is unable to find any messages under these keys, or if no 047 * message source is provided, default messages (in English) will be used 048 * instead. 049 * </p> 050 * 051 * @author Kevin Stembridge 052 * @since 0.3.0 053 * 054 * @see ProgressMonitor 055 * 056 */ 057 public class ProgressMonitoringBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 058 059 /** 060 * The message key used to retrieve the message to be sent to the progress 061 * monitor when the application context begins loading. 062 */ 063 public static final String LOADING_APP_CONTEXT_KEY = "progress.loading.applicationContext"; 064 065 /** 066 * The message key used to retrieve the message to be sent to the progress 067 * monitor for each bean that is loaded. 068 */ 069 public static final String LOADING_BEAN_KEY = "progress.loading.bean"; 070 071 private static final Log logger = LogFactory.getLog(ProgressMonitoringBeanFactoryPostProcessor.class); 072 073 private final ProgressMonitor progressMonitor; 074 075 private final MessageSource messageSource; 076 077 private final String loadingAppContextMessage; 078 079 /** 080 * Creates a new {@code ProgressMonitoringBeanFactoryPostProcessor} that 081 * will report the progress of loading the beans in a bean factory to the 082 * given progress monitor, optionally providing internationalized messages. 083 * 084 * @param progressMonitor The progress monitor that will be notified of 085 * progress while processing the bean factory. 086 * @param messageSource The message source that will be used to resolve 087 * messages to be displayed by the progress monitor. May be null. 088 * 089 * @throws IllegalArgumentException if {@code progressMonitor} is null. 090 */ 091 public ProgressMonitoringBeanFactoryPostProcessor(ProgressMonitor progressMonitor, MessageSource messageSource) { 092 093 Assert.notNull(progressMonitor, "The ProgressMonitor cannot be null"); 094 095 this.progressMonitor = progressMonitor; 096 this.messageSource = messageSource; 097 this.loadingAppContextMessage = getLoadingAppContextMessage(); 098 099 } 100 101 /** 102 * Notifies this instance's associated progress monitor of progress made 103 * while processing the given bean factory. 104 * 105 * @param beanFactory the bean factory that is to be processed. 106 */ 107 public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException { 108 109 if (beanFactory == null) { 110 return; 111 } 112 113 String[] beanNames = beanFactory.getBeanDefinitionNames(); 114 int singletonBeanCount = 0; 115 116 for (int i = 0; i < beanNames.length; i++) { 117 // using beanDefinition to check singleton property because when 118 // accessing through 119 // context (applicationContext.isSingleton(beanName)), bean will be 120 // created already, 121 // possibly bypassing other BeanFactoryPostProcessors 122 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanNames[i]); 123 124 if (beanDefinition.isSingleton()) { 125 singletonBeanCount++; 126 } 127 128 } 129 130 this.progressMonitor.taskStarted(this.loadingAppContextMessage, singletonBeanCount); 131 132 beanFactory.addBeanPostProcessor(new ProgressMonitoringBeanPostProcessor(beanFactory)); 133 134 } 135 136 private String getLoadingAppContextMessage() { 137 138 String defaultMessage = "Loading Application Context ..."; 139 140 if (this.messageSource == null) { 141 return defaultMessage; 142 } 143 144 return this.messageSource.getMessage(LOADING_APP_CONTEXT_KEY, null, defaultMessage, null); 145 146 } 147 148 private class ProgressMonitoringBeanPostProcessor implements BeanPostProcessor { 149 150 private final ConfigurableBeanFactory beanFactory; 151 152 private ProgressMonitoringBeanPostProcessor(ConfigurableBeanFactory beanFactory) { 153 Assert.notNull(beanFactory, "The bean factory cannot be null"); 154 this.beanFactory = beanFactory; 155 } 156 157 /** 158 * A default implementation that performs no operation on the bean. 159 */ 160 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 161 return bean; 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 168 169 if (logger.isTraceEnabled()) { 170 logger.trace("BEGIN: postProcessBeforeInitialization(" + beanName + ")"); 171 } 172 173 if (this.beanFactory.containsLocalBean(beanName)) { 174 String loadingBeanMessage = getLoadingBeanMessage(beanName); 175 progressMonitor.subTaskStarted(loadingBeanMessage); 176 progressMonitor.worked(1); 177 } 178 179 logger.trace("END: postProcessBeforeInitialization()"); 180 return bean; 181 182 } 183 184 private String getLoadingBeanMessage(String beanName) { 185 186 String defaultMessage = "Loading " + beanName + " ..."; 187 188 if (messageSource == null) { 189 return defaultMessage; 190 } 191 192 Object[] args = { beanName }; 193 194 return messageSource.getMessage(LOADING_BEAN_KEY, args, defaultMessage, null); 195 196 } 197 198 } 199 200 }