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
015package org.apache.tapestry5.internal.services;
016
017import org.apache.tapestry5.func.F;
018import org.apache.tapestry5.func.Mapper;
019import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
020import org.apache.tapestry5.ioc.internal.util.OneShotLock;
021import org.apache.tapestry5.ioc.services.ThreadCleanupListener;
022import org.apache.tapestry5.ioc.util.AvailableValues;
023import org.apache.tapestry5.ioc.util.Stack;
024import org.apache.tapestry5.ioc.util.UnknownValueException;
025import org.apache.tapestry5.services.Environment;
026
027import java.util.LinkedList;
028import java.util.List;
029import java.util.Map;
030
031/**
032 * A non-threadsafe implementation (expects to use the "perthread" service lifecyle).
033 */
034public 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}