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.context.support; 018 019import java.util.LinkedHashMap; 020import java.util.Map; 021import javax.servlet.ServletContext; 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 ServletContext, i.e. for global web application attributes. 030 * 031 * <p>This differs from traditional Spring singletons in that it exposes attributes in the 032 * ServletContext. 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 ServletContextScope implements Scope, DisposableBean { 049 050 private final ServletContext servletContext; 051 052 private final Map<String, Runnable> destructionCallbacks = new LinkedHashMap<String, Runnable>(); 053 054 055 /** 056 * Create a new Scope wrapper for the given ServletContext. 057 * @param servletContext the ServletContext to wrap 058 */ 059 public ServletContextScope(ServletContext servletContext) { 060 Assert.notNull(servletContext, "ServletContext must not be null"); 061 this.servletContext = servletContext; 062 } 063 064 065 @Override 066 public Object get(String name, ObjectFactory<?> objectFactory) { 067 Object scopedObject = this.servletContext.getAttribute(name); 068 if (scopedObject == null) { 069 scopedObject = objectFactory.getObject(); 070 this.servletContext.setAttribute(name, scopedObject); 071 } 072 return scopedObject; 073 } 074 075 @Override 076 public Object remove(String name) { 077 Object scopedObject = this.servletContext.getAttribute(name); 078 if (scopedObject != null) { 079 this.servletContext.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}