/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.ioc.tests.wiring.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.enterprise.inject.Any;
import javax.inject.Named;
import org.jboss.errai.ioc.client.JsArray;
import org.jboss.errai.ioc.client.WindowInjectionContext;
import org.jboss.errai.ioc.client.WindowInjectionContextStorage;
import org.jboss.errai.ioc.client.container.DynamicAnnotation;
import org.jboss.errai.ioc.client.container.Factory;
import org.jboss.errai.ioc.client.container.IOC;
import org.jboss.errai.ioc.client.container.IOCResolutionException;
import org.jboss.errai.ioc.client.container.JsTypeProvider;
import org.jboss.errai.ioc.client.container.Proxy;
import org.jboss.errai.ioc.client.container.SyncBeanDef;
import org.jboss.errai.ioc.client.container.SyncBeanManagerImpl;
import org.jboss.errai.ioc.client.test.AbstractErraiIOCTest;
import org.jboss.errai.ioc.tests.wiring.client.res.AlternativeImpl;
import org.jboss.errai.ioc.tests.wiring.client.res.ConcreteWindowScopedJsType;
import org.jboss.errai.ioc.tests.wiring.client.res.ConsumesProducedJsType;
import org.jboss.errai.ioc.tests.wiring.client.res.DuplicateInterface;
import org.jboss.errai.ioc.tests.wiring.client.res.ExternalJsTypeIface;
import org.jboss.errai.ioc.tests.wiring.client.res.ExternalJsTypeImpl;
import org.jboss.errai.ioc.tests.wiring.client.res.ExternalTestModule;
import org.jboss.errai.ioc.tests.wiring.client.res.InternallySatisfiedImpl;
import org.jboss.errai.ioc.tests.wiring.client.res.InternallySatisfiedJsTypeIface;
import org.jboss.errai.ioc.tests.wiring.client.res.InternallyUnsatisfiedJsTypeIface;
import org.jboss.errai.ioc.tests.wiring.client.res.JsSubTypeWithDuplicateInterface;
import org.jboss.errai.ioc.tests.wiring.client.res.JsSuperTypeWithDuplicateInterface;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeConsumer;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeDependentBean;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeDependentInterface;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeNamedBean;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeSingletonBean;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeSingletonInterface;
import org.jboss.errai.ioc.tests.wiring.client.res.JsTypeWithQualifiers;
import org.jboss.errai.ioc.tests.wiring.client.res.MultipleImplementationsJsType;
import org.jboss.errai.ioc.tests.wiring.client.res.NativeConcreteJsType;
import org.jboss.errai.ioc.tests.wiring.client.res.NativeConcreteJsTypeWithConstructorDependency;
import org.jboss.errai.ioc.tests.wiring.client.res.NativeConcreteJsTypeWithFieldDependency;
import org.jboss.errai.ioc.tests.wiring.client.res.NativeTypeTestModule;
import org.jboss.errai.ioc.tests.wiring.client.res.ProducedJsType;
import org.jboss.errai.ioc.tests.wiring.client.res.QualWithMultiMembers;
import org.jboss.errai.ioc.tests.wiring.client.res.UnimplementedType;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.user.client.Timer;
public class JsTypeInjectionTest extends AbstractErraiIOCTest {
@Override
public String getModuleName() {
return "org.jboss.errai.ioc.tests.wiring.IOCWiringTests";
}
@Override
protected void gwtSetUp() throws Exception {
WindowInjectionContextStorage.reset();
final WindowInjectionContext windowInjContext = WindowInjectionContextStorage.createOrGet();
windowInjContext.addBeanProvider(MultipleImplementationsJsType.class.getName(), new JsTypeProvider<Object>() {
@Override
public Object getInstance() {
return new AlternativeImpl(1);
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
windowInjContext.addBeanProvider(MultipleImplementationsJsType.class.getName(), new JsTypeProvider<Object>() {
@Override
public Object getInstance() {
return new AlternativeImpl(2);
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
windowInjContext.addBeanProvider(InternallyUnsatisfiedJsTypeIface.class.getName(),
new JsTypeProvider<InternallyUnsatisfiedJsTypeIface>() {
@Override
public InternallyUnsatisfiedJsTypeIface getInstance() {
return new InternallyUnsatisfiedJsTypeIface() {
@Override
public String message() {
return "external";
}
};
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
windowInjContext.addBeanProvider(ExternalJsTypeImpl.class.getName(),
new JsTypeProvider<ExternalJsTypeIface>() {
@Override
public ExternalJsTypeIface getInstance() {
return new ExternalJsTypeIface() {
@Override
public String message() {
return "external";
}
};
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
windowInjContext.addBeanProvider(ConcreteWindowScopedJsType.class.getName(),
new JsTypeProvider<ConcreteWindowScopedJsType>() {
@Override
public ConcreteWindowScopedJsType getInstance() {
return new ConcreteWindowScopedJsType() {
@Override
public String message() {
return "external";
}
};
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
windowInjContext.addSuperTypeAlias(ExternalJsTypeIface.class.getName(), ExternalJsTypeImpl.class.getName());
super.gwtSetUp();
}
public void testSingletonJsTypeInWindowContext() {
final WindowInjectionContext wndContext = WindowInjectionContextStorage.createOrGet();
final Object bean1 = wndContext.getBean(JsTypeSingletonBean.class.getName());
assertNotNull("@JsType bean was not registered in window context", bean1);
final Object bean2 = wndContext.getBean(JsTypeSingletonInterface.class.getName());
assertNotNull("@JsType bean was not registered using its interface", bean2);
assertSame(bean1, bean2);
}
public void testDependentJsTypeInWindowContext() {
final WindowInjectionContext wndContext = WindowInjectionContextStorage.createOrGet();
final Object bean1 = wndContext.getBean(JsTypeDependentBean.class.getName());
assertNotNull("@JsType bean was not registered in window context", bean1);
final Object bean2 = wndContext.getBean(JsTypeDependentInterface.class.getName());
assertNotNull("@JsType bean was not registered using its interface", bean2);
assertNotSame(bean1, bean2);
}
@SuppressWarnings("rawtypes")
public void testNamedJsTypeInWindowContext() {
final WindowInjectionContext wndContext = WindowInjectionContextStorage.createOrGet();
final Object bean1 = wndContext.getBean(JsTypeNamedBean.class.getName());
assertNotNull("@JsType bean was not registered in window context", bean1);
final Object bean2 = wndContext.getBean("olaf");
assertNotNull("@JsType bean was not registered using its interface", bean2);
assertSame(bean1, bean2);
final Collection<SyncBeanDef> beans = new ArrayList<>(IOC.getBeanManager().lookupBeans("olaf"));
beans.addAll(IOC.getBeanManager().lookupBeans(JsTypeNamedBean.class.getName()));
for (final SyncBeanDef bean : beans) {
assertEquals("olaf", bean.getName());
}
}
@SuppressWarnings("rawtypes")
public void testNoDuplicateJsTypeThroughBeanManager() {
final WindowInjectionContext wndContext = WindowInjectionContextStorage.createOrGet();
final Object bean1 = wndContext.getBean(JsTypeNamedBean.class.getName());
assertNotNull("@JsType bean was not registered in window context", bean1);
final Collection<SyncBeanDef> beans = IOC.getBeanManager().lookupBeans(JsTypeNamedBean.class.getName());
assertEquals(1, beans.size());
assertEquals("olaf", beans.iterator().next().getName());
assertSame(bean1, beans.iterator().next().getInstance());
}
public void testConsumingOfUnimplementedJsType() throws Exception {
injectScriptThenRun(() -> {
final UnimplementedType ref = new UnimplementedType() {
@Override
public void overloaded(final Object obj) {
}
@Override
protected void overloaded() {
}
};
final WindowInjectionContext wndContext = WindowInjectionContextStorage.createOrGet();
wndContext.addBeanProvider("org.jboss.errai.ioc.tests.wiring.client.res.UnimplementedType", new JsTypeProvider<Object>(){
@Override
public Object getInstance() {
return ref;
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
final SyncBeanDef<JsTypeConsumer> consumer = IOC.getBeanManager().lookupBean(JsTypeConsumer.class);
assertSame(ref, Factory.maybeUnwrapProxy(consumer.getInstance().getIface()));
});
}
public void testNativeJsTypesNotInWindowContext() throws Exception {
injectScriptThenRun(() -> {
final WindowInjectionContext context = WindowInjectionContextStorage.createOrGet();
try {
context.getBean("org.jboss.errai.ioc.tests.wiring.client.res.NativeConcreteJsType");
fail("There should not be a provider in the WindowInjectionContext for NativeConcreteJsType.");
} catch (final IOCResolutionException ex) {
}
});
}
public void testNoArgInstantiableNativeJsTypeIsInjectable() throws Exception {
injectScriptThenRun(() -> {
try {
final NativeTypeTestModule module = IOC.getBeanManager().lookupBean(NativeTypeTestModule.class).getInstance();
final NativeConcreteJsType instance = module.nativeConcreteJsType;
assertEquals("Not the expected implementation (in native.js).", "I am a native type!", instance.message());
} catch (final IOCResolutionException ex) {
fail("Precondition failed: Problem looking up test module.");
}
});
}
public void testInstantiableNativeJsTypeWithConstructorDependencyIsInjectable() throws Exception {
injectScriptThenRun(() -> {
try {
final NativeTypeTestModule module = IOC.getBeanManager().lookupBean(NativeTypeTestModule.class).getInstance();
final NativeConcreteJsTypeWithConstructorDependency instance = module.nativeWithConstructorDep;
assertNotNull(instance.get());
assertEquals("Not the expected implementation (in native.js).", "I am a native type!", instance.get().message());
} catch (final IOCResolutionException ex) {
fail("Precondition failed: Problem looking up test module.");
}
});
}
public void testInstantiableNativeJsTypeWithFieldDependencyIsInjectable() throws Exception {
injectScriptThenRun(() -> {
try {
final NativeTypeTestModule module = IOC.getBeanManager().lookupBean(NativeTypeTestModule.class).getInstance();
final NativeConcreteJsTypeWithFieldDependency instance = module.nativeWithFieldDep;
assertNotNull(instance.get());
assertEquals("Not the expected implementation (in native.js).", "I am a native type!", instance.get().message());
} catch (final IOCResolutionException ex) {
fail("Precondition failed: Problem looking up test module.");
}
});
}
public void testProducerMethodNativeOfJsType() throws Exception {
injectScriptThenRun(() -> {
try {
final NativeTypeTestModule module = IOC.getBeanManager().lookupBean(NativeTypeTestModule.class).getInstance();
assertNotNull(module.producedNativeIface);
assertEquals("Not the expected implementation (in native.js).", "please", module.producedNativeIface.getMagicWord());
} catch (final IOCResolutionException ex) {
fail("Precondition failed: Problem looking up test module.");
}
});
}
public void testProducerMethodOfJsType() throws Exception {
final ConsumesProducedJsType consumer = IOC.getBeanManager().lookupBean(ConsumesProducedJsType.class).getInstance();
assertNotNull(consumer.instance);
assertTrue(consumer.instance instanceof ProducedJsType);
assertEquals(1, IOC.getBeanManager().lookupBeans(ProducedJsType.class).size());
}
public void testMultipleJsTypeImplementations() throws Exception {
Collection<SyncBeanDef<MultipleImplementationsJsType>> beans = IOC.getBeanManager().lookupBeans(MultipleImplementationsJsType.class);
assertEquals(2, beans.size());
final Set<Integer> values = new HashSet<>();
for (final SyncBeanDef<MultipleImplementationsJsType> bean : beans) {
final MultipleImplementationsJsType instance = bean.getInstance();
assertTrue(instance instanceof AlternativeImpl);
values.add(instance.value());
}
assertEquals(new HashSet<>(Arrays.asList(1, 2)), values);
WindowInjectionContextStorage.createOrGet().addBeanProvider(MultipleImplementationsJsType.class.getName(), new JsTypeProvider<Object>() {
@Override
public Object getInstance() {
return new AlternativeImpl(3);
}
@Override
public String getName() {
return null;
}
@Override
public String getFactoryName() {
return null;
}
@Override
public JsArray<String> getQualifiers() {
return new JsArray<String>(new String[0]);
}
});
values.clear();
beans = IOC.getBeanManager().lookupBeans(MultipleImplementationsJsType.class);
for (final SyncBeanDef<MultipleImplementationsJsType> bean : beans) {
values.add(bean.getInstance().value());
}
assertEquals(new HashSet<>(Arrays.asList(1, 2, 3)), values);
}
public void testNoDuplicatesWithSuperTypeAliases() throws Exception {
final JsArray<JsTypeProvider<?>> providers = WindowInjectionContextStorage.createOrGet()
.getProviders(DuplicateInterface.class.getName());
assertEquals(2, providers.length());
assertEquals(
new HashSet<>(Arrays.asList(
JsSubTypeWithDuplicateInterface.class.getName(),
JsSuperTypeWithDuplicateInterface.class.getName())),
new HashSet<>(Arrays.asList(
providers.get(0).getInstance().getClass().getName(),
providers.get(1).getInstance().getClass().getName())));
}
public void testLocalBeanSatisfiesJsTypeInterfaceForDefaultInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.defaultSatisfiedIface);
assertTrue(module.defaultSatisfiedIface instanceof InternallySatisfiedImpl);
assertSame(module.defaultSatisfiedIface, IOC.getBeanManager().lookupBean(InternallySatisfiedJsTypeIface.class).getInstance());
}
public void testLocalBeanSatisfiesJsTypeInterfaceForExternalInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.externalSatisfiedIface);
assertTrue(module.externalSatisfiedIface instanceof InternallySatisfiedImpl);
assertSame(module.externalSatisfiedIface,
IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance().externalSatisfiedIface);
}
public void testWindowContextBeanSatisfiesJsTypeInterfaceForDefaultInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.defaultUnsatisfiedIface);
assertEquals("external", module.defaultUnsatisfiedIface.message());
}
public void testWindowContextBeanSatisfiesJsTypeInterfaceForExternalInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.defaultUnsatisfiedIface);
assertEquals("external", module.defaultUnsatisfiedIface.message());
}
public void testImplNotPublishedUnderExternalJsTypeIfaceWhenAlreadyInWindowContext() throws Exception {
assertEquals(1, WindowInjectionContextStorage.createOrGet().getProviders(ExternalJsTypeIface.class.getName()).length());
}
public void testLocalBeanSatisfiesExternalJsTypeInterfaceForDefaultInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.defaultExternalIface);
assertEquals(ExternalJsTypeImpl.class.getSimpleName(), module.defaultExternalIface.message());
assertTrue(module.defaultExternalIface instanceof ExternalJsTypeImpl);
}
public void testWindowContextBeanSatisfiesExternalJsTypeInterfaceForExternalInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.externalExternalIface);
assertEquals("external", module.externalExternalIface.message());
assertFalse(module.externalExternalIface instanceof ExternalJsTypeImpl);
}
public void testWindowContextBeanSatisfiesConcreteWindowScopedJsTypeForDefaultInjectionSite() throws Exception {
final ExternalTestModule module = IOC.getBeanManager().lookupBean(ExternalTestModule.class).getInstance();
assertNotNull(module.defaultConcreteWindowScopedJsType);
assertEquals("external", module.defaultConcreteWindowScopedJsType.message());
}
public void testQualifiersOnJsType() throws Exception {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Collection<SyncBeanDef<JsTypeWithQualifiers>> beans = (Collection) ((SyncBeanManagerImpl) IOC.getBeanManager())
.lookupBeans(JsTypeWithQualifiers.class.getName(), true);
SyncBeanDef<JsTypeWithQualifiers> beanDef = null;
for (final SyncBeanDef<JsTypeWithQualifiers> bd : beans) {
if (bd.isDynamic()) {
beanDef = bd;
}
}
assertNotNull("No bean JS bean def found", beanDef);
@SuppressWarnings({ "unchecked", "rawtypes" })
final Set<DynamicAnnotation> quals = (Set) beanDef.getQualifiers();
assertEquals(3, quals.size());
final Set<String> notYetFound = new HashSet<>(Arrays.asList(Named.class.getName(),
QualWithMultiMembers.class.getName(), Any.class.getName()));
for (final DynamicAnnotation qual : quals) {
final Map<String, String> members = qual.getMembers();
if (Any.class.getName().equals(qual.getName())) {
assertEquals(0, members.size());
notYetFound.remove(Any.class.getName());
}
else if (Named.class.getName().equals(qual.getName())) {
assertEquals(1, members.size());
assertEquals("Moogah", members.get("value"));
notYetFound.remove(Named.class.getName());
}
else if (QualWithMultiMembers.class.getName().equals(qual.getName())) {
assertEquals(3, members.size());
assertEquals("1", members.get("num"));
assertEquals("foo", members.get("text"));
assertEquals(Arrays.toString(new String[] {JsTypeWithQualifiers.class.getName()}), members.get("clazzes"));
notYetFound.remove(QualWithMultiMembers.class.getName());
}
}
assertEquals(0, notYetFound.size());
}
public void testSingletonJsTypeIsNotProxied() throws Exception {
final NativeTypeTestModule module = IOC.getBeanManager().lookupBean(NativeTypeTestModule.class).getInstance();
assertEquals("Sanity check for correct bean failed.", "please", module.singletonJsType.magicWord());
assertFalse("Singleton JS bean should not be proxied.", module.singletonJsType instanceof Proxy);
}
private void injectScriptThenRun(final Runnable test) {
final String scriptUrl = getScriptUrl();
final Timer timeoutFail = new Timer() {
@Override
public void run() {
fail("Timed out waiting to load " + scriptUrl);
}
};
timeoutFail.schedule(9000);
delayTestFinish(10000);
ScriptInjector.fromUrl(scriptUrl)
.setWindow(ScriptInjector.TOP_WINDOW)
.setCallback(new Callback<Void, Exception>() {
@Override
public void onFailure(final Exception reason) {
timeoutFail.cancel();
fail("Could not load " + scriptUrl);
}
@Override
public void onSuccess(final Void result) {
try {
test.run();
finishTest();
} finally {
timeoutFail.cancel();
}
}
}).inject();
}
private String getScriptUrl() {
final String scriptUrl = GWT.getModuleBaseForStaticFiles() + "native.js";
return scriptUrl;
}
}