View Javadoc

1   package org.apache.onami.scopes;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import com.google.inject.Key;
23  import com.google.inject.Provider;
24  import com.google.inject.ProvisionException;
25  import com.google.inject.Scope;
26  import com.google.inject.internal.CircularDependencyProxy;
27  
28  import java.util.HashMap;
29  import java.util.Map;
30  
31  class ConcurrentLazySingletonScopeImpl
32      implements Scope
33  {
34      private static final Object NULL = new Object();
35  
36      private final Map<Key<?>, LockRecord> locks = new HashMap<Key<?>, LockRecord>();
37  
38      public <T> Provider<T> scope( final Key<T> key, final Provider<T> creator )
39      {
40          return new Provider<T>()
41          {
42              /*
43               * The lazily initialized singleton instance. Once set, this will either have type T or will
44               * be equal to NULL.
45               */
46              private volatile Object instance;
47  
48              // DCL on a volatile is safe as of Java 5, which we obviously require.
49              @SuppressWarnings( "DoubleCheckedLocking" )
50              public T get()
51              {
52                  if ( instance == null )
53                  {
54                      final LockRecord lock = getLock( key );
55                      try
56                      {
57                          //noinspection SynchronizationOnLocalVariableOrMethodParameter
58                          synchronized ( lock )
59                          {
60                              if ( instance == null )
61                              {
62                                  T provided = creator.get();
63  
64                                  // don't remember proxies; these exist only to serve circular dependencies
65                                  if ( provided instanceof CircularDependencyProxy )
66                                  {
67                                      return provided;
68                                  }
69  
70                                  Object providedOrSentinel = ( provided == null ) ? NULL : provided;
71                                  if ( ( instance != null ) && ( instance != providedOrSentinel ) )
72                                  {
73                                      throw new ProvisionException( "Provider was reentrant while creating a singleton" );
74                                  }
75  
76                                  instance = providedOrSentinel;
77                              }
78                          }
79                      }
80                      finally
81                      {
82                          releaseLock( lock, key );
83                      }
84                  }
85  
86                  Object localInstance = instance;
87                  // This is safe because instance has type T or is equal to NULL
88                  @SuppressWarnings( { "unchecked", "UnnecessaryLocalVariable" } ) T returnedInstance =
89                      ( localInstance != NULL ) ? (T) localInstance : null;
90                  return returnedInstance;
91              }
92  
93              public String toString()
94              {
95                  return String.format( "%s[%s]", creator, instance );
96              }
97          };
98      }
99  
100     private LockRecord getLock( Key<?> key )
101     {
102         synchronized ( locks )
103         {
104             LockRecord lock = locks.get( key );
105             if ( lock == null )
106             {
107                 lock = new LockRecord();
108                 locks.put( key, lock );
109             }
110             ++lock.useCount;
111             return lock;
112         }
113     }
114 
115     private void releaseLock( LockRecord lock, Key<?> key )
116     {
117         synchronized ( locks )
118         {
119             if ( --lock.useCount <= 0 )
120             {
121                 locks.remove( key );
122             }
123         }
124     }
125 
126     @Override
127     public String toString()
128     {
129         return "ConcurrentLazySingletonScope.SCOPE";
130     }
131 
132     private static class LockRecord
133     {
134         private int useCount = 0;
135     }
136 }