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 }