All Downloads are FREE. Search and download functionalities are using the official Maven repository.

roboguice.inject.ContextScope Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Copyright 2009 Michael Burton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */
package roboguice.inject;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import roboguice.util.RoboContext;

import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;

import android.app.Application;
import android.content.Context;
import android.content.ContextWrapper;

/**
 * Scopes the injector based on the current context.
 *
 * Any usage of this class must call #enter(Context) before performing any operations with the
 * injector, and do so within a synchronized block on the ContextScope.class, eg:
 *
 * synchronized(ContextScope.class) {
 *     scope.enter(context);
 *
 *     // do something, eg.
 *     // injector.injectMembers(this);
 * }
 *
 * If you're using ContextScopedRoboInjector (which is the RoboGuice default), this is done for you automatically.
 *
 * If you're trying to use a Provider, you must either use ContextScopedProvider instead, or do your own synchronization
 * and scope.enter() call.
 *
 * @see ContextScopedRoboInjector
 * @author Mike Burton
 */
public class ContextScope implements Scope {
    protected ThreadLocal>> contextThreadLocal = new ThreadLocal>>();
    protected Map,Object> applicationScopedObjects = new HashMap, Object>();
    protected Application application;

    public ContextScope(Application application) {
        this.application = application;
        enter(application);
    }

    /**
     * You MUST perform any injector operations inside a synchronized(ContextScope.class) block that starts with
     * scope.enter(context) if working in a multithreaded environment
     *
     * @see ContextScope
     * @see ContextScopedRoboInjector
     * @see ContextScopedProvider
     * @param context the context to enter
     */
    public void enter(Context context) {

        // BUG synchronizing on ContextScope.class may be overly conservative
        synchronized (ContextScope.class) {

            final Stack> stack = getContextStack();
            final Map,Object> map = getScopedObjectMap(context);

            // Mark this thread as for this context
            stack.push(new WeakReference(context));

            // Add the context to the scope for key Context, Activity, etc.
            Class c = context.getClass();
            do {
                map.put(Key.get(c), context);
                c = c.getSuperclass();
            } while( c!=Object.class );
        }

    }

    public void exit(Context context) {
        synchronized (ContextScope.class) {
            final Stack> stack = getContextStack();
            final Context c = stack.pop().get();
            if( c!=null && c!=context )
                throw new IllegalArgumentException(String.format("Scope for %s must be opened before it can be closed",context));
        }
    }

    public  Provider scope(final Key key, final Provider unscoped) {
        return new Provider() {
            public T get() {
                synchronized (ContextScope.class) {
                    final Stack> stack = getContextStack();
                    final Context context = stack.peek().get(); // The context should never be finalized as long as the provider is still in memory
                    final Map, Object> objectsForScope = getScopedObjectMap(context);
                    if( objectsForScope==null )
                        return null;  // May want to consider throwing an exception here (if provider is used after onDestroy())

                    @SuppressWarnings({"unchecked"}) T current = (T) objectsForScope.get(key);
                    if (current==null && !objectsForScope.containsKey(key)) {
                        current = unscoped.get();
                        objectsForScope.put(key, current);
                    }

                    return current;
                }
            }
        };

    }


    public Stack> getContextStack() {
        Stack> stack = contextThreadLocal.get();
        if( stack==null ) {
            stack = new Stack>();
            contextThreadLocal.set(stack);
        }
        return stack;
    }

    protected Map,Object> getScopedObjectMap(final Context origContext) {
        Context context = origContext;
        while( !(context instanceof RoboContext) && !(context instanceof Application) && context instanceof ContextWrapper )
            context = ((ContextWrapper)context).getBaseContext();

        // Special case for application so that users don't have to manually set up application subclasses
        if( context instanceof Application )
            return applicationScopedObjects;


        if( !(context instanceof RoboContext) )
            throw new IllegalArgumentException(String.format("%s does not appear to be a RoboGuice context (instanceof RoboContext)",origContext));

        return ((RoboContext)context).getScopedObjectMap();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy