001package org.apache.maven.session.scope.internal; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.Collections; 023import java.util.LinkedList; 024import java.util.Map; 025 026import com.google.common.collect.ImmutableMap; 027import com.google.common.collect.Maps; 028import com.google.inject.Key; 029import com.google.inject.OutOfScopeException; 030import com.google.inject.Provider; 031import com.google.inject.Scope; 032import com.google.inject.util.Providers; 033 034public class SessionScope 035 implements Scope 036{ 037 /** 038 * @since 3.3.0 039 */ 040 public static class Memento 041 { 042 final Map<Key<?>, Provider<?>> seeded; 043 044 Memento( final Map<Key<?>, Provider<?>> seeded ) 045 { 046 this.seeded = ImmutableMap.copyOf( seeded ); 047 } 048 } 049 050 private static final Provider<Object> SEEDED_KEY_PROVIDER = new Provider<Object>() 051 { 052 public Object get() 053 { 054 throw new IllegalStateException(); 055 } 056 }; 057 058 private static final class ScopeState 059 { 060 public final Map<Key<?>, Provider<?>> seeded = Maps.newHashMap(); 061 062 public final Map<Key<?>, Object> provided = Maps.newHashMap(); 063 } 064 065 private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<>(); 066 067 public void enter() 068 { 069 LinkedList<ScopeState> stack = values.get(); 070 if ( stack == null ) 071 { 072 stack = new LinkedList<>(); 073 values.set( stack ); 074 } 075 stack.addFirst( new ScopeState() ); 076 } 077 078 /** 079 * @since 3.3.0 080 */ 081 public void enter( Memento memento ) 082 { 083 enter(); 084 getScopeState().seeded.putAll( memento.seeded ); 085 } 086 087 private ScopeState getScopeState() 088 { 089 LinkedList<ScopeState> stack = values.get(); 090 if ( stack == null || stack.isEmpty() ) 091 { 092 throw new IllegalStateException(); 093 } 094 return stack.getFirst(); 095 } 096 097 public void exit() 098 { 099 final LinkedList<ScopeState> stack = values.get(); 100 if ( stack == null || stack.isEmpty() ) 101 { 102 throw new IllegalStateException(); 103 } 104 stack.removeFirst(); 105 if ( stack.isEmpty() ) 106 { 107 values.remove(); 108 } 109 } 110 111 /** 112 * @since 3.3.0 113 */ 114 public Memento memento() 115 { 116 LinkedList<ScopeState> stack = values.get(); 117 return new Memento( stack != null ? stack.getFirst().seeded : Collections.<Key<?>, Provider<?>>emptyMap() ); 118 } 119 120 public <T> void seed( Class<T> clazz, Provider<T> value ) 121 { 122 getScopeState().seeded.put( Key.get( clazz ), value ); 123 } 124 125 public <T> void seed( Class<T> clazz, final T value ) 126 { 127 getScopeState().seeded.put( Key.get( clazz ), Providers.of( value ) ); 128 } 129 130 public <T> Provider<T> scope( final Key<T> key, final Provider<T> unscoped ) 131 { 132 return new Provider<T>() 133 { 134 @SuppressWarnings( "unchecked" ) 135 public T get() 136 { 137 LinkedList<ScopeState> stack = values.get(); 138 if ( stack == null || stack.isEmpty() ) 139 { 140 throw new OutOfScopeException( "Cannot access " + key + " outside of a scoping block" ); 141 } 142 143 ScopeState state = stack.getFirst(); 144 145 Provider<?> seeded = state.seeded.get( key ); 146 147 if ( seeded != null ) 148 { 149 return (T) seeded.get(); 150 } 151 152 T provided = (T) state.provided.get( key ); 153 if ( provided == null && unscoped != null ) 154 { 155 provided = unscoped.get(); 156 state.provided.put( key, provided ); 157 } 158 159 return provided; 160 } 161 }; 162 } 163 164 @SuppressWarnings( { "unchecked" } ) 165 public static <T> Provider<T> seededKeyProvider() 166 { 167 return (Provider<T>) SEEDED_KEY_PROVIDER; 168 } 169}