001/*
002 * Copyright 2012-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 *      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,
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.boot.builder;
018
019import java.lang.ref.WeakReference;
020
021import org.springframework.beans.BeansException;
022import org.springframework.boot.builder.ParentContextApplicationContextInitializer.ParentContextAvailableEvent;
023import org.springframework.context.ApplicationContext;
024import org.springframework.context.ApplicationContextAware;
025import org.springframework.context.ApplicationListener;
026import org.springframework.context.ConfigurableApplicationContext;
027import org.springframework.context.event.ContextClosedEvent;
028import org.springframework.core.Ordered;
029import org.springframework.util.ObjectUtils;
030
031/**
032 * Listener that closes the application context if its parent is closed. It listens for
033 * refresh events and grabs the current context from there, and then listens for closed
034 * events and propagates it down the hierarchy.
035 *
036 * @author Dave Syer
037 * @author Eric Bottard
038 */
039public class ParentContextCloserApplicationListener
040                implements ApplicationListener<ParentContextAvailableEvent>,
041                ApplicationContextAware, Ordered {
042
043        private int order = Ordered.LOWEST_PRECEDENCE - 10;
044
045        private ApplicationContext context;
046
047        @Override
048        public int getOrder() {
049                return this.order;
050        }
051
052        @Override
053        public void setApplicationContext(ApplicationContext context) throws BeansException {
054                this.context = context;
055        }
056
057        @Override
058        public void onApplicationEvent(ParentContextAvailableEvent event) {
059                maybeInstallListenerInParent(event.getApplicationContext());
060        }
061
062        private void maybeInstallListenerInParent(ConfigurableApplicationContext child) {
063                if (child == this.context
064                                && child.getParent() instanceof ConfigurableApplicationContext) {
065                        ConfigurableApplicationContext parent = (ConfigurableApplicationContext) child
066                                        .getParent();
067                        parent.addApplicationListener(createContextCloserListener(child));
068                }
069        }
070
071        /**
072         * Subclasses may override to create their own subclass of ContextCloserListener. This
073         * still enforces the use of a weak reference.
074         * @param child the child context
075         * @return the {@link ContextCloserListener} to use
076         */
077        protected ContextCloserListener createContextCloserListener(
078                        ConfigurableApplicationContext child) {
079                return new ContextCloserListener(child);
080        }
081
082        /**
083         * {@link ApplicationListener} to close the context.
084         */
085        protected static class ContextCloserListener
086                        implements ApplicationListener<ContextClosedEvent> {
087
088                private WeakReference<ConfigurableApplicationContext> childContext;
089
090                public ContextCloserListener(ConfigurableApplicationContext childContext) {
091                        this.childContext = new WeakReference<>(childContext);
092                }
093
094                @Override
095                public void onApplicationEvent(ContextClosedEvent event) {
096                        ConfigurableApplicationContext context = this.childContext.get();
097                        if ((context != null)
098                                        && (event.getApplicationContext() == context.getParent())
099                                        && context.isActive()) {
100                                context.close();
101                        }
102                }
103
104                @Override
105                public boolean equals(Object obj) {
106                        if (this == obj) {
107                                return true;
108                        }
109                        if (obj == null) {
110                                return false;
111                        }
112                        if (obj instanceof ContextCloserListener) {
113                                ContextCloserListener other = (ContextCloserListener) obj;
114                                return ObjectUtils.nullSafeEquals(this.childContext.get(),
115                                                other.childContext.get());
116                        }
117                        return super.equals(obj);
118                }
119
120                @Override
121                public int hashCode() {
122                        return ObjectUtils.nullSafeHashCode(this.childContext.get());
123                }
124
125        }
126
127}