001/*
002 * Copyright 2002-2012 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.web.portlet.context;
018
019import java.util.LinkedHashMap;
020import java.util.Map;
021import javax.portlet.PortletContext;
022
023import org.springframework.beans.factory.DisposableBean;
024import org.springframework.beans.factory.ObjectFactory;
025import org.springframework.beans.factory.config.Scope;
026import org.springframework.util.Assert;
027
028/**
029 * {@link Scope} wrapper for a PortletContext, i.e. for global web application attributes.
030 *
031 * <p>This differs from traditional Spring singletons in that it exposes attributes in the
032 * PortletContext. Those attributes will get destroyed whenever the entire application
033 * shuts down, which might be earlier or later than the shutdown of the containing Spring
034 * ApplicationContext.
035 *
036 * <p>The associated destruction mechanism relies on a
037 * {@link org.springframework.web.context.ContextCleanupListener} being registered in
038 * {@code web.xml}. Note that {@link org.springframework.web.context.ContextLoaderListener}
039 * includes ContextCleanupListener's functionality.
040 *
041 * <p>This scope is registered as default scope with key
042 * {@link org.springframework.web.context.WebApplicationContext#SCOPE_APPLICATION "application"}.
043 *
044 * @author Juergen Hoeller
045 * @since 3.0
046 * @see org.springframework.web.context.ContextCleanupListener
047 */
048public class PortletContextScope implements Scope, DisposableBean {
049
050        private final PortletContext portletContext;
051
052        private final Map<String, Runnable> destructionCallbacks = new LinkedHashMap<String, Runnable>();
053
054
055        /**
056         * Create a new Scope wrapper for the given PortletContext.
057         * @param portletContext the PortletContext to wrap
058         */
059        public PortletContextScope(PortletContext portletContext) {
060                Assert.notNull(portletContext, "PortletContext must not be null");
061                this.portletContext = portletContext;
062        }
063
064
065        @Override
066        public Object get(String name, ObjectFactory<?> objectFactory) {
067                Object scopedObject = this.portletContext.getAttribute(name);
068                if (scopedObject == null) {
069                        scopedObject = objectFactory.getObject();
070                        this.portletContext.setAttribute(name, scopedObject);
071                }
072                return scopedObject;
073        }
074
075        @Override
076        public Object remove(String name) {
077                Object scopedObject = this.portletContext.getAttribute(name);
078                if (scopedObject != null) {
079                        this.portletContext.removeAttribute(name);
080                        this.destructionCallbacks.remove(name);
081                        return scopedObject;
082                }
083                else {
084                        return null;
085                }
086        }
087
088        @Override
089        public void registerDestructionCallback(String name, Runnable callback) {
090                this.destructionCallbacks.put(name, callback);
091        }
092
093        @Override
094        public Object resolveContextualObject(String key) {
095                return null;
096        }
097
098        @Override
099        public String getConversationId() {
100                return null;
101        }
102
103
104        /**
105         * Invoke all registered destruction callbacks.
106         * To be called on ServletContext shutdown.
107         * @see org.springframework.web.context.ContextCleanupListener
108         */
109        @Override
110        public void destroy() {
111                for (Runnable runnable : this.destructionCallbacks.values()) {
112                        runnable.run();
113                }
114                this.destructionCallbacks.clear();
115        }
116
117}