001/**
002 * Copyright 2005-2015 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 */
016package org.kuali.rice.krad.util;
017
018import org.kuali.rice.krad.UserSession;
019import org.kuali.rice.krad.web.form.UifFormManager;
020import org.kuali.rice.core.framework.util.ApplicationThreadLocal;
021
022import java.util.HashMap;
023import java.util.LinkedList;
024import java.util.Map;
025import java.util.concurrent.Callable;
026
027/**
028 * Holds all of our thread local variables and accessors for those
029 *
030 * @author Kuali Rice Team (rice.collab@kuali.org)
031 */
032public final class GlobalVariables {
033
034    private static ThreadLocal<LinkedList<GlobalVariables>> GLOBAL_VARIABLES_STACK = new ApplicationThreadLocal<LinkedList<GlobalVariables>>() {
035        protected LinkedList<GlobalVariables> initialValue() {
036            LinkedList<GlobalVariables> globalVariablesStack = new LinkedList<GlobalVariables>();
037            globalVariablesStack.add(new GlobalVariables());
038            return globalVariablesStack;
039        }
040    };
041
042    public static GlobalVariables getCurrentGlobalVariables() {
043        return GLOBAL_VARIABLES_STACK.get().getLast();
044    }
045
046    public static void injectGlobalVariables(GlobalVariables globalVariables) {
047        GLOBAL_VARIABLES_STACK.get().add(globalVariables);
048    }
049
050    public static GlobalVariables popGlobalVariables() {
051        return GLOBAL_VARIABLES_STACK.get().removeLast();
052    }
053
054    static GlobalVariables pushGlobalVariables() {
055        GlobalVariables vars = new GlobalVariables();
056        GLOBAL_VARIABLES_STACK.get().add(vars);
057        return vars;
058    }
059
060    static void reset() {
061        LinkedList<GlobalVariables> stack = GLOBAL_VARIABLES_STACK.get();
062        stack.clear();
063        stack.add(new GlobalVariables());
064    }
065
066    private UserSession userSession = null;
067    private String hideSessionFromTestsMessage = null;
068    private MessageMap messageMap = new MessageMap();
069    private Map<String,Object> requestCache = new HashMap<String, Object>();
070    private UifFormManager uifFormManager = null;
071
072    private static ThreadLocal<HashMap<String, AuditCluster>> auditErrorMaps = new ApplicationThreadLocal<HashMap<String, AuditCluster>>() {
073        @Override
074        protected HashMap<String, AuditCluster> initialValue() {
075                return new HashMap<String, AuditCluster>();
076        }
077    };
078
079    private GlobalVariables() {}
080
081    /**
082     * @return the UserSession that has been assigned to this thread of execution it is important that this not be called by
083     *         anything that lives outside
084     */
085    public static UserSession getUserSession() {
086        GlobalVariables vars = getCurrentGlobalVariables();
087        String message = vars.hideSessionFromTestsMessage;
088        if (message != null) {
089            throw new RuntimeException(message);
090        }
091        return vars.userSession;
092    }
093
094    /**
095     * Sets an error message for tests that try to use the session without declaring it.
096     * This method should be use by only KualiTestBase, not by other test code and especially not by production code.
097     *
098     * @param message the detail to throw, or null to allow access to the session
099     */
100    public static void setHideSessionFromTestsMessage(String message) {
101        GlobalVariables vars = getCurrentGlobalVariables();
102        vars.hideSessionFromTestsMessage = message;
103    }
104
105    /**
106     * sets the userSession object into the global variable for this thread
107     *
108     * @param userSession
109     */
110    public static void setUserSession(UserSession userSession) {
111        GlobalVariables vars = getCurrentGlobalVariables();
112        vars.userSession = userSession;
113    }
114
115    public static MessageMap getMessageMap() {
116        GlobalVariables vars = getCurrentGlobalVariables();
117        return vars.messageMap;
118    }
119
120    /**
121     * Merges a message map into the global variables error map
122     * @param messageMap
123     */
124    public static void mergeErrorMap(MessageMap messageMap) {
125        getMessageMap().getErrorMessages().putAll(messageMap.getErrorMessages());
126        getMessageMap().getWarningMessages().putAll(messageMap.getWarningMessages());
127        getMessageMap().getInfoMessages().putAll(messageMap.getInfoMessages());
128    }
129
130    /**
131     * Sets a new (clean) MessageMap
132     *
133     * @param messageMap
134     */
135    public static void setMessageMap(MessageMap messageMap) {
136        GlobalVariables vars = getCurrentGlobalVariables();
137        vars.messageMap = messageMap;
138    }
139
140    public static Object getRequestCache(String cacheName) {
141        GlobalVariables vars = getCurrentGlobalVariables();
142        return vars.requestCache.get(cacheName);
143    }
144
145    public static void setRequestCache(String cacheName, Object cacheObject) {
146        GlobalVariables vars = getCurrentGlobalVariables();
147        synchronized (vars.requestCache) {
148            vars.requestCache.put(cacheName, cacheObject);
149        }
150    }
151
152    /**
153     * Retrieves the {@link org.kuali.rice.krad.web.form.UifFormManager} which can be used to store and remove forms
154     * from the session
155     *
156     * @return UifFormManager
157     */
158    public static UifFormManager getUifFormManager() {
159        GlobalVariables vars = getCurrentGlobalVariables();
160        return vars.uifFormManager;
161    }
162
163    /**
164     * Sets a {@link org.kuali.rice.krad.web.form.UifFormManager} for the current thread
165     *
166     * @param uifFormManager
167     */
168    public static void setUifFormManager(UifFormManager uifFormManager) {
169        GlobalVariables vars = getCurrentGlobalVariables();
170        vars.uifFormManager = uifFormManager;
171    }
172
173    /**
174     * @return ArrayList containing audit error messages.
175     */
176    public static Map<String, AuditCluster> getAuditErrorMap() {
177        return auditErrorMaps.get();
178    }
179
180    /**
181     * Sets a new (clean) AuditErrorList
182     *
183     * @param errorMap
184     */
185    public static void setAuditErrorMap(HashMap<String, AuditCluster> errorMap) {
186        auditErrorMaps.set(errorMap);
187    }
188
189    /**
190     * Clears out GlobalVariable objects with the exception of the UserSession
191     */
192    public static void clear() {
193        GlobalVariables vars = getCurrentGlobalVariables();
194        vars.messageMap = new MessageMap();
195        vars.requestCache = new HashMap<String,Object>();
196        auditErrorMaps.set(new HashMap<String, AuditCluster>());
197    }
198
199    /**
200     * Pushes a new GlobalVariables object onto the ThreadLocal GlobalVariables stack, invokes the runnable,
201     * and pops the GlobalVariables off in a finally clause
202     * 
203     * @param <T> callable return type
204     * @param callable the code to run under a new set of GlobalVariables
205     * @return return value from callable
206     * @throws Exception from {@link Callable#call()}
207     */
208    public static <T> T doInNewGlobalVariables(Callable<T> callable) throws Exception {
209        return doInNewGlobalVariables(null, callable);
210    }
211
212    /**
213     * Convenience method that creates a new GlobalVariables stack frame, initialized with the provided
214     * UserSession (which may be the previous UserSession).
215     *
216     * @param <T> callable return type
217     * @param userSession the UserSession to initialize the new frame with (may be null)
218     * @param callable the code to run under a new set of GlobalVariables
219     * @return return value from callable
220     * @throws Exception from {@link Callable#call()}
221     */
222    public static <T> T doInNewGlobalVariables(UserSession userSession, Callable<T> callable) throws Exception {
223        try {
224            GlobalVariables vars = pushGlobalVariables();
225            if (userSession != null) {
226                vars.userSession = userSession;
227            }
228            return callable.call();
229        } finally {
230            popGlobalVariables();
231        }
232    }
233}