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    }