/*
* Copyright (C) 2009 Google Inc.
*
* 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 com.google.guiceberry.controllable;
import java.lang.reflect.Type;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.util.Types;
/**
* To implement controllable injections, what is fundamentally required is a way
* for client (test) to send controllable-injection-related data to the server.
*
* <p>Out-of-the-box, GuiceBerry offers a {@link StaticMapInjectionController},
* which is ideal when you run both test and server in the same ClassLoader/JVM.
*
* <p>When that is not possible, there are other ways to send this sort of data,
* such as HTTP {@link javax.servlet.http.Cookie}s, {@link java.io.File}s in a
* shared disk, shared database tables and whatnot.
*
* <p>This class makes the implementation of these strategies possible. Each
* of these would simply require an implementation of the
* {@link IcStrategy.ClientSupport} and {@link IcStrategy.ServerSupport}
* interfaces.
*
* <p>Note that each of these classes will be instantiated by a different
* {@link com.google.inject.Injector}.
*
* <p>Also note that these implementations are not directly exposed to the
* end-user, but rather are a support for the Controllable Injection framework.
* Note that the
* {@link IcStrategy.ClientSupport#setOverride(ControllableId, Object)} and
* {@link IcStrategy.ClientSupport#resetOverride(ControllableId)} methods are
* <em>indirectly</em> exposed to the end-user through
* {@link InjectionController#setOverride(Object)} and {@link InjectionController#resetOverride()}
* respectively.
*
* <p>Finally, note that some strategies might be fundamentally more "capable"
* than others. E.g. a non-serializable class would pose a problem for a
* Cookie-based strategy, while the default (shared variable) strategy has no
* difficulty in supporting it. In fact, the default strategy is fundamentally
* the most capable strategy, and, thus, we say it's ideal.
*
* @author Luiz-Otavio Zorzella
*/
public class IcStrategy {
/**
* The "client side" implementation of a Controllable Injection
* {@link IcStrategy}.
*/
public interface ClientSupport {
/**
* Called every time a test controls an injection by calling
* {@link InjectionController#setOverride(Object)}.
*
* <p>From this moment on, a server injector's call to
* {@link IcStrategy.ServerSupport#isControlled(ControllableId)} for this
* {@code controllableId} should return true and a call to
* {@link IcStrategy.ServerSupport#getOverride(ControllableId, Provider)}
* for this {@code controllableId} should return {@code override}.
*
* <p>It is considered OK for a test to call
* {@link InjectionController#setOverride(Object)} multiple times, so this method
* must also support it.
*/
<T> void setOverride(ControllableId<T> controllableId, T override);
/**
* This method stops the controlling of an injection, i.e. it "undoes"
* <em>all</em> the previous calls to
* {@link #setOverride(ControllableId, Object)}.
*
* <p>This method is called automatically by the framework when a test which
* has previously called {@link InjectionController#setOverride(Object)} is torn down,
* but may also be called indirectly through {@link InjectionController#resetOverride()}.
*
* <p>This method may be called multiple times in a row (i.e. you may be
* asked to reset an override that is not currently being controlled), and
* should support this.
*/
<T> void resetOverride(ControllableId<T> controllableId);
}
/**
* The "server side" implementation of a Controllable Injection
* {@link IcStrategy}.
*/
public interface ServerSupport {
/**
* Returns true if the {@code controllableId} is currently being controlled.
*
* <p>When this returns true, the framework will call
* {@link #getOverride(ControllableId, Provider)} to fulfill an injection,
* otherwise it will bypass it.
*/
<T> boolean isControlled(ControllableId<T> controllableId);
/**
* Returns the current override for this Injection. The framework will
* never call this method unless {@link #isControlled(ControllableId)}
* returns true, so calling this method while not controlling an injection
* is illegal and the outcome is unspecified (though it's conceivably
* possible to subvert the system with a race condition).
*
* @param delegate what the server would return in absence of this injection
* being controlled. This param is expected to be ignored by most of the
* {@link IcStrategy}s, but might be useful for certain advanced purposes.
*/
<T> T getOverride(ControllableId<T> controllableId, Provider<? extends T> delegate);
}
private final Class<? extends IcStrategy.ClientSupport> clientSupportClass;
private final Class<? extends IcStrategy.ServerSupport> serverSupportClass;
public IcStrategy(
Class<? extends IcStrategy.ClientSupport> icStrategyClientSupportClass,
Class<? extends IcStrategy.ServerSupport> icStrategyServerSupportClass
) {
this.clientSupportClass = icStrategyClientSupportClass;
this.serverSupportClass = icStrategyServerSupportClass;
}
Class<? extends IcStrategy.ClientSupport> clientSupportClass() {
return clientSupportClass;
}
Class<? extends IcStrategy.ServerSupport> serverSupportClass() {
return serverSupportClass;
}
static Key<?> wrap(Type raw, Key<?> annotationHolder) {
Type type = Types.newParameterizedType(
raw, annotationHolder.getTypeLiteral().getType());
if (annotationHolder.getAnnotation() != null) {
return Key.get(type, annotationHolder.getAnnotation());
} else if (annotationHolder.getAnnotationType() != null) {
return Key.get(type, annotationHolder.getAnnotationType());
} else {
return Key.get(type);
}
}
}