xapi.inject.X_Inject Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xapi-gwt Show documentation
Show all versions of xapi-gwt Show documentation
This module exists solely to package all other gwt modules into a single
uber jar. This makes deploying to non-mavenized targets much easier.
Of course, you would be wise to inherit your dependencies individually;
the uber jar is intended for projects like collide,
which have complex configuration, and adding many jars would be a pain.
The newest version!
/*
* Copyright 2012, We The Internet Ltd.
*
* All rights reserved.
*
* Distributed under a modified BSD License as follow:
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution, unless otherwise
* agreed to in a written document signed by a director of We The Internet Ltd.
*
* Neither the name of We The Internet nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* Public displays of software using this code may choose to credit the contributors,
* but are not required to give attribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
*/
package xapi.inject;
import static xapi.util.X_Runtime.isJava;
import java.util.ServiceLoader;
import javax.inject.Provider;
import xapi.annotation.gwt.MagicMethod;
import xapi.annotation.inject.SingletonDefault;
import xapi.annotation.inject.SingletonOverride;
import xapi.annotation.reflect.KeepMethod;
import xapi.except.NotConfiguredCorrectly;
import xapi.inject.api.Injector;
import xapi.inject.impl.JavaInjector;
import xapi.util.api.ReceivesValue;
/**
* A static accessor class for in an instance of {@link Injector}.
*
*
*
* Classes marked with {@link SingletonDefault} or {@link SingletonOverride} are usable by X_Inject.
* Once a class has been marked as a service provider for a given interface or class,
* it can be used in cross-platform code as follows:
*
*
*
* static interface MyService{}
*
*
*
* @SingletonDefault(implFor=MyService.class)
* static class MyServiceImpl implements MyService{}
*
*
*
* MyService service = X_Inject.singleton(MyService.class);//returns MyServiceImpl
*
*
*
* All actual injection occurs in implementations of {@link Injector}.
*
*
*
* The pure-java solution uses {@link JavaInjector}, which is based on the
* jre6 {@link ServiceLoader}, with some tweaks to match the gwt injector behavior.
* Files with the name equal to the interface or class being injected are written
* to META-INF/singletons for services to be instantiated only once per runtime.
* META-INF/instances is for objects that should be new copies for every request.
*
* A third form as META-INF/threadlocal may be provided for thread-local or
* process-local services, to help separated code share state without explicit
* reference passing. This option will be considered if the concurrency model
* is able to offer lifecycle events to ensure the gc of process-local singletons.
*
*
*
* The gwt solution uses a generated extension of {@link JsInjector} using {@link AsyncProxy}.
* See {@link xapi.dev.generators.GwtDevGenerator} and
* its subclass {@link xapi.dev.generators.AbstractInjectionGenerator}
* in the xapi-gwt-inject module.
*
*
*
* Unlike ServiceLoader, which presents an iterable of available services,
* X_Inject currently only returns the highest priority (top of the list) service,
* and it only returns lazily-loaded singletons (which work with code splitting).
*
*
*
* Note, all static service accessors in XApi are prefixed with X_,
* to make exposed services easier to discover in autocomplete.
*
*
*
* @author James X. Nelson ([email protected], @james)
*
*/
@KeepMethod
public class X_Inject{
public static void initialize(final Object o) {
// JavaInjector.initialize(o);
}
@KeepMethod
@MagicMethod(doNotVisit=true)
public static T instance(final Class super T> cls){
if (isJava()) {
return xapi.inject.impl.JavaInjector.instance(cls);
} else {
throw notConfiguredCorrectly();
}
}
/**
* Returns an injected singleton service implementation object.
*
* WARNING: When running in compiled gwt mode with code splitting,
* if you use the asynchronous provider method
* {@link #singletonAsync(Class, ReceivesValue)}, then the synchronous providers,
* {@link #singleton(Class)} and {@link #singletonLazy(Class)} will return null until
* the code split containing the service is loaded.
*
* TODO: use AsyncProxy from
*
* If you have ANY doubt about whether a service is already loaded or not,
* please prefer the async provider method {@link #singletonAsync(Class, ReceivesValue)
*
*
*
* @param cls - The service interface / base class to inject.
* @return - An instance of the requested class, based on DI annotations.
*
*
*
* There must be at least one class on your classpath which is
* annotated with {@link SingletonDefault} or {@link SingletonOverride},
* and which implements or overrides the service being provided.
*
*
*
* You are recommended to use only interfaces in service definitions,
* but you are free to use classes if you wish.
*
*
*
* It is valid to annotate a class as it's own default implementation.
*
*/
@KeepMethod
@MagicMethod(doNotVisit=true)
public static T singleton(final Class super T> cls){
if (isJava()) {
final Provider provider = xapi.inject.impl.JavaInjector.singletonLazy(cls);
return provider.get();
} else {
throw notConfiguredCorrectly();
}
}
@KeepMethod
@MagicMethod(doNotVisit=true)
public static void singletonAsync(final Class super T> cls,final Class extends ReceivesValue> callback){
if (isJava()){
// We expose a Class super T> api for all injection methods,
// but in the case of async callback injection, we have to allow
// extensions of ReceivesValue, so concrete classes will map correctly.
// Thus, we must coerce the bounds of the callback class (mostly for gwt)
@SuppressWarnings("unchecked")
final
Class super ReceivesValue> receiverCls = Class.class.cast(callback);
// we normally enforce "give supertype, get subtype", but in the case of callbacks,
// it's "give supertype and a concrete subclass of ReceivesValue,
// and we'll inject the callback and the singleton.
final ReceivesValue receiver = singleton(receiverCls);
singletonAsync(cls, receiver);
} else {
throw notConfiguredCorrectly();
}
}
/**
*
* Asynchronously loads an injected singleton service implementation object.
* This method uses gwt code splitting to put each service into it's own island loader.
* It will create a split for every service, but with the closure compiler and CodeSplitter2,
* the compiler will do a very good job of modularizing and optimizing split points.
*
*
*
* WARNING: If you use async loading for a service provider in one place,
* and then use synchronous methods {@link #singleton(Class)} or {@link #singletonLazy(Class)},
* the synchronous methods will return null in gwt production mode until after the split point
* is loaded. If you have any doubts about whether a given service is already loaded,
* always prefer this asynchronous provider method instead of the sync ones.
*
*
*
* In compiled gwt, this is treated as a magic method which generators provider classes.
* In gwt dev mode, it defers to a single class which provides all injected object.
* In pure java mode, it relies on java.util.ServiceLoader.
* Running either gwt dev or a full gwt compile will generate META-INF/singletons for you.
*
*
*
* @param cls - The service interface / base class to inject.
* @param callback - The {@link ReceivesValue} to notify when the object is loaded.
*
*
*
* There must be at least one class on your classpath which is
* annotated with {@link SingletonDefault} or {@link SingletonOverride},
* and which implements or overrides the service being provided.
*
*
*
* You are recommended to use only interfaces in service definitions,
* but you are free to use classes if you wish.
*
*
*
* It is valid to annotate a class as it's own default implementation.
*
*/
@KeepMethod
@MagicMethod(doNotVisit=true)
public static void singletonAsync(final Class super T> cls, final ReceivesValue callback){
if (isJava()) {
final Provider provider = xapi.inject.impl.JavaInjector.singletonLazy(cls);
callback.set(provider.get());
} else {
throw notConfiguredCorrectly();
}
}
/**
* This is a magic method, like gwt.create.
* It is replaced in UnifyAst with references to generated classes.
*
* In pure java implementations,
* it defers to either a generated provider class for gwt-dev,
* and to ServiceLoader.load() for pure java implementations.
*
* @param cls - The interface or base class to be injected
* @return - A provider which will return the loaded injected class.
*
* WARNING: In gwt production mode,
* if you use code splitting to move a provider into it's own async splitpoint,
* any other split points which use synchronous access will return null until loaded.
*
* If you have any doubts about the timing and load sequence,
* always prefer the {@link #singletonAsync(Class, ReceivesValue)} method.
*
*/
@KeepMethod
@MagicMethod(doNotVisit=true)
public static Provider singletonLazy(final Class super T> cls) {
if (isJava()) {
return xapi.inject.impl.JavaInjector.singletonLazy(cls);
} else {
throw notConfiguredCorrectly();
}
}
private static NotConfiguredCorrectly notConfiguredCorrectly() {
return new NotConfiguredCorrectly("xapi-gwt(-api) must be above gwt-dev on classpath");
}
private X_Inject() {}
/** TODO: use well-known scopes, like UserAgent or Language to create "scope worlds".
public static enum InjectionScopes {
// Some handy default scopes to share.
// You are free to use any scope you want;
// the Global scope will be checked by default if there is no match
// in the requested scope.
UserAgent, Language, Authenticated, Request, User, Global;
}
// public static T scopedInject(Class scope, Class type);
// public static void scopedInjectAsync(Class scope, Class type, ReceivesValue callback);
*/
}