001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache 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 *      https://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,
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
017package org.springframework.context.support;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.beans.BeansException;
023import org.springframework.context.ApplicationContext;
024import org.springframework.context.ApplicationContextAware;
025import org.springframework.context.ApplicationContextException;
026import org.springframework.lang.Nullable;
027import org.springframework.util.Assert;
028
029/**
030 * Convenient superclass for application objects that want to be aware of
031 * the application context, e.g. for custom lookup of collaborating beans
032 * or for context-specific resource access. It saves the application
033 * context reference and provides an initialization callback method.
034 * Furthermore, it offers numerous convenience methods for message lookup.
035 *
036 * <p>There is no requirement to subclass this class: It just makes things
037 * a little easier if you need access to the context, e.g. for access to
038 * file resources or to the message source. Note that many application
039 * objects do not need to be aware of the application context at all,
040 * as they can receive collaborating beans via bean references.
041 *
042 * <p>Many framework classes are derived from this class, particularly
043 * within the web support.
044 *
045 * @author Rod Johnson
046 * @author Juergen Hoeller
047 * @see org.springframework.web.context.support.WebApplicationObjectSupport
048 */
049public abstract class ApplicationObjectSupport implements ApplicationContextAware {
050
051        /** Logger that is available to subclasses. */
052        protected final Log logger = LogFactory.getLog(getClass());
053
054        /** ApplicationContext this object runs in. */
055        @Nullable
056        private ApplicationContext applicationContext;
057
058        /** MessageSourceAccessor for easy message access. */
059        @Nullable
060        private MessageSourceAccessor messageSourceAccessor;
061
062
063        @Override
064        public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
065                if (context == null && !isContextRequired()) {
066                        // Reset internal context state.
067                        this.applicationContext = null;
068                        this.messageSourceAccessor = null;
069                }
070                else if (this.applicationContext == null) {
071                        // Initialize with passed-in context.
072                        if (!requiredContextClass().isInstance(context)) {
073                                throw new ApplicationContextException(
074                                                "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
075                        }
076                        this.applicationContext = context;
077                        this.messageSourceAccessor = new MessageSourceAccessor(context);
078                        initApplicationContext(context);
079                }
080                else {
081                        // Ignore reinitialization if same context passed in.
082                        if (this.applicationContext != context) {
083                                throw new ApplicationContextException(
084                                                "Cannot reinitialize with different application context: current one is [" +
085                                                this.applicationContext + "], passed-in one is [" + context + "]");
086                        }
087                }
088        }
089
090        /**
091         * Determine whether this application object needs to run in an ApplicationContext.
092         * <p>Default is "false". Can be overridden to enforce running in a context
093         * (i.e. to throw IllegalStateException on accessors if outside a context).
094         * @see #getApplicationContext
095         * @see #getMessageSourceAccessor
096         */
097        protected boolean isContextRequired() {
098                return false;
099        }
100
101        /**
102         * Determine the context class that any context passed to
103         * {@code setApplicationContext} must be an instance of.
104         * Can be overridden in subclasses.
105         * @see #setApplicationContext
106         */
107        protected Class<?> requiredContextClass() {
108                return ApplicationContext.class;
109        }
110
111        /**
112         * Subclasses can override this for custom initialization behavior.
113         * Gets called by {@code setApplicationContext} after setting the context instance.
114         * <p>Note: Does <i>not</i> get called on re-initialization of the context
115         * but rather just on first initialization of this object's context reference.
116         * <p>The default implementation calls the overloaded {@link #initApplicationContext()}
117         * method without ApplicationContext reference.
118         * @param context the containing ApplicationContext
119         * @throws ApplicationContextException in case of initialization errors
120         * @throws BeansException if thrown by ApplicationContext methods
121         * @see #setApplicationContext
122         */
123        protected void initApplicationContext(ApplicationContext context) throws BeansException {
124                initApplicationContext();
125        }
126
127        /**
128         * Subclasses can override this for custom initialization behavior.
129         * <p>The default implementation is empty. Called by
130         * {@link #initApplicationContext(org.springframework.context.ApplicationContext)}.
131         * @throws ApplicationContextException in case of initialization errors
132         * @throws BeansException if thrown by ApplicationContext methods
133         * @see #setApplicationContext
134         */
135        protected void initApplicationContext() throws BeansException {
136        }
137
138
139        /**
140         * Return the ApplicationContext that this object is associated with.
141         * @throws IllegalStateException if not running in an ApplicationContext
142         */
143        @Nullable
144        public final ApplicationContext getApplicationContext() throws IllegalStateException {
145                if (this.applicationContext == null && isContextRequired()) {
146                        throw new IllegalStateException(
147                                        "ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
148                }
149                return this.applicationContext;
150        }
151
152        /**
153         * Obtain the ApplicationContext for actual use.
154         * @return the ApplicationContext (never {@code null})
155         * @throws IllegalStateException in case of no ApplicationContext set
156         * @since 5.0
157         */
158        protected final ApplicationContext obtainApplicationContext() {
159                ApplicationContext applicationContext = getApplicationContext();
160                Assert.state(applicationContext != null, "No ApplicationContext");
161                return applicationContext;
162        }
163
164        /**
165         * Return a MessageSourceAccessor for the application context
166         * used by this object, for easy message access.
167         * @throws IllegalStateException if not running in an ApplicationContext
168         */
169        @Nullable
170        protected final MessageSourceAccessor getMessageSourceAccessor() throws IllegalStateException {
171                if (this.messageSourceAccessor == null && isContextRequired()) {
172                        throw new IllegalStateException(
173                                        "ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
174                }
175                return this.messageSourceAccessor;
176        }
177
178}