001 // Copyright 2006, 2007, 2008, 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.internal.services; 016 017 import org.apache.tapestry5.func.F; 018 import org.apache.tapestry5.func.Mapper; 019 import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 020 import org.apache.tapestry5.ioc.internal.util.OneShotLock; 021 import org.apache.tapestry5.ioc.services.ThreadCleanupListener; 022 import org.apache.tapestry5.ioc.util.AvailableValues; 023 import org.apache.tapestry5.ioc.util.Stack; 024 import org.apache.tapestry5.ioc.util.UnknownValueException; 025 import org.apache.tapestry5.services.Environment; 026 027 import java.util.LinkedList; 028 import java.util.List; 029 import java.util.Map; 030 031 /** 032 * A non-threadsafe implementation (expects to use the "perthread" service lifecyle). 033 */ 034 public class EnvironmentImpl implements Environment, ThreadCleanupListener 035 { 036 037 // My generics mojo breaks down when we talk about the key and the value being related 038 // types. 039 040 private Map<Class, LinkedList> typeToStack = CollectionFactory.newMap(); 041 042 // Support for cloak/decloak. Cloaking pushes the current typeToStack map onto the stack 043 // and creates a new, empyt, Map to replace it. Decloaking discards the current map 044 // and replaces it with the top map on the stack. 045 046 private final Stack<Map<Class, LinkedList>> cloakStack = CollectionFactory.newStack(); 047 048 private final OneShotLock lock = new OneShotLock(); 049 050 @SuppressWarnings("unchecked") 051 private <T> LinkedList<T> stackFor(Class<T> type) 052 { 053 lock.check(); 054 055 LinkedList<T> result = typeToStack.get(type); 056 057 if (result == null) 058 { 059 result = CollectionFactory.newLinkedList(); 060 typeToStack.put(type, result); 061 } 062 063 return result; 064 } 065 066 public <T> T peek(Class<T> type) 067 { 068 LinkedList<T> stack = stackFor(type); 069 070 return stack.isEmpty() ? null : stack.getFirst(); 071 } 072 073 public <T> T peekRequired(Class<T> type) 074 { 075 T result = peek(type); 076 077 if (result == null) 078 { 079 List<Class> types = CollectionFactory.newList(); 080 for (Map.Entry<Class, LinkedList> e : typeToStack.entrySet()) 081 { 082 LinkedList list = e.getValue(); 083 084 if (list != null && !list.isEmpty()) 085 types.add(e.getKey()); 086 } 087 088 throw new UnknownValueException(String.format("No object of type %s is available from the Environment.", type.getName()), 089 new AvailableValues("Environmentals", 090 F.flow(typeToStack.keySet()).map(new Mapper<Class, String>() 091 { 092 public String map(Class element) 093 { 094 return element.getName(); 095 } 096 }).toList())); 097 } 098 099 return result; 100 } 101 102 public <T> T pop(Class<T> type) 103 { 104 LinkedList<T> stack = stackFor(type); 105 106 return stack.removeFirst(); 107 } 108 109 public <T> T push(Class<T> type, T instance) 110 { 111 LinkedList<T> stack = stackFor(type); 112 113 T result = stack.isEmpty() ? null : stack.getFirst(); 114 115 stack.addFirst(instance); 116 117 return result; 118 } 119 120 public void clear() 121 { 122 throw new IllegalStateException("Environment.clear() is no longer supported."); 123 } 124 125 public void threadDidCleanup() 126 { 127 lock.lock(); 128 } 129 130 public void cloak() 131 { 132 cloakStack.push(typeToStack); 133 134 typeToStack = CollectionFactory.newMap(); 135 } 136 137 public void decloak() 138 { 139 typeToStack = cloakStack.pop(); 140 } 141 }