blob: 637a1412ec6de9c5d98e1666b00c130fc6623eab [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.polygene.runtime.injection;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Predicate;
import org.apache.polygene.api.activation.ActivationException;
import org.apache.polygene.api.common.ConstructionException;
import org.apache.polygene.api.common.Optional;
import org.apache.polygene.api.identity.Identity;
import org.apache.polygene.api.identity.StringIdentity;
import org.apache.polygene.api.injection.scope.Service;
import org.apache.polygene.api.mixin.Mixins;
import org.apache.polygene.api.object.ObjectFactory;
import org.apache.polygene.api.service.ServiceComposite;
import org.apache.polygene.api.service.ServiceImporterException;
import org.apache.polygene.api.service.ServiceReference;
import org.apache.polygene.api.service.qualifier.AnnotationQualifier;
import org.apache.polygene.api.service.qualifier.IdentifiedBy;
import org.apache.polygene.api.service.qualifier.Qualifier;
import org.apache.polygene.bootstrap.ApplicationAssembly;
import org.apache.polygene.bootstrap.LayerAssembly;
import org.apache.polygene.bootstrap.ModuleAssembly;
import org.apache.polygene.bootstrap.ServiceDeclaration;
import org.apache.polygene.bootstrap.SingletonAssembler;
import org.junit.Test;
import static org.apache.polygene.api.common.Visibility.application;
import static org.apache.polygene.api.common.Visibility.layer;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* Test the @Service injection annotation
*/
public class ServiceInjectionTest
{
@Test
public void testInjectService()
throws Exception
{
SingletonAssembler assembly = new SingletonAssembler(
module -> {
module.services( MyServiceComposite.class )
.identifiedBy( "Foo" )
.setMetaInfo( new ServiceName( "Foo" ) );
module.services( MyServiceComposite2.class )
.identifiedBy( "Bar" )
.setMetaInfo( new ServiceName( "Bar" ) );
module.services( StringService.class, LongService.class );
module.objects( ServiceUser.class );
}
);
testInjection( assembly );
}
private void testInjection( SingletonAssembler assembly )
{
ObjectFactory factory = assembly.module();
ServiceUser user = factory.newObject( ServiceUser.class );
assertEquals( "X", user.testSingle() );
assertThat( user.testIdentity(), equalTo( StringIdentity.identity( "Foo" ) ) );
assertEquals( "FooX", user.testServiceReference() );
assertEquals( "Bar", user.testQualifier() );
assertEquals( "A", user.testStringIterable() );
assertEquals( new Long( 1L ), user.testLongIterable() );
assertEquals( "FooXBarX", user.testIterableServiceReferences() );
assertEquals( "XX", user.testIterable() );
}
@Test
public void testInjectionServiceBetweenModules()
throws ActivationException
{
SingletonAssembler assembly = new SingletonAssembler(
module -> {
module.services( MyServiceComposite.class )
.identifiedBy( "Foo" )
.setMetaInfo( new ServiceName( "Foo" ) );
module.services( StringService.class, LongService.class );
module.objects( ServiceUser.class );
ModuleAssembly module2 = module.layer().module( "Other module" );
ServiceDeclaration service2Decl = module2.services( MyServiceComposite.class );
service2Decl.identifiedBy( "Bar" ).setMetaInfo( new ServiceName( "Bar" ) ).visibleIn( layer );
ServiceDeclaration service3Decl = module2.services( MyServiceComposite2.class );
service3Decl.identifiedBy( "Boo" ).setMetaInfo( new ServiceName( "Boo" ) );
}
);
testInjection( assembly );
}
@Test
public void testInjectionServiceBetweenLayers()
throws ActivationException
{
SingletonAssembler assembly = new SingletonAssembler(
module -> {
module.services( MyServiceComposite.class )
.identifiedBy( "Foo" )
.setMetaInfo( new ServiceName( "Foo" ) );
module.services( StringService.class, LongService.class );
LayerAssembly layerAssembly = module.layer();
module.objects( ServiceUser.class );
ApplicationAssembly applicationAssembly = layerAssembly.application();
LayerAssembly layer2Assembly = applicationAssembly.layer( "Other layer" );
layerAssembly.uses( layer2Assembly );
ModuleAssembly module2 = layer2Assembly.module( "Other module" );
ServiceDeclaration service2Decl = module2.services( MyServiceComposite2.class );
service2Decl.identifiedBy( "Bar" ).setMetaInfo( new ServiceName( "Bar" ) ).visibleIn( application );
}
);
testInjection( assembly );
}
@Test( expected = ConstructionException.class )
public void testMissingServiceDependency()
throws ActivationException
{
// No service fulfils the dependency injection -> fail to create application
new SingletonAssembler( module -> module.objects( ServiceUser.class ) )
.module().newObject( ServiceUser.class );
}
@Mixins( MyServiceMixin.class )
public interface MyServiceComposite
extends MyService, ServiceComposite
{
}
public interface MyServiceComposite2
extends MyServiceComposite
{
}
public interface MyService
{
String doStuff();
}
public static class MyServiceMixin
implements MyService
{
public String doStuff()
{
return "X";
}
}
public static class ServiceUser
extends AbstractServiceUser<String>
{
}
public static class AbstractServiceUser<T>
{
@Service
MyService service;
@Service
MyServiceComposite serviceComposite;
@Service
Iterable<MyService> services;
@Service
ServiceReference<MyService> serviceRef;
@Service
Iterable<ServiceReference<MyService>> serviceRefs;
@Service
@IdentifiedBy( "Bar" )
ServiceReference<MyService> qualifiedService;
@Service
@IdentifiedBy( "Bar" )
Iterable<ServiceReference<MyService>> qualifiedServiceRefs;
@Optional
@Service
MyServiceMixin optionalService12;
@Service
Foo<Long> longService;
@Service
Foo<T> stringService;
public String testSingle()
{
return service.doStuff();
}
public Identity testIdentity()
{
return serviceComposite.identity().get();
}
public String testIterable()
{
String str = "";
for( MyService myService : services )
{
str += myService.doStuff();
}
return str;
}
public String testServiceReference()
throws ServiceImporterException
{
ServiceName info = serviceRef.metaInfo( ServiceName.class );
return info.getName() + serviceRef.get().doStuff();
}
public String testIterableServiceReferences()
throws ServiceImporterException
{
String str = "";
for( ServiceReference<MyService> serviceReference : serviceRefs )
{
str += serviceReference.metaInfo( ServiceName.class ).getName();
str += serviceReference.get().doStuff();
}
return str;
}
public String testQualifier()
{
return qualifiedService.metaInfo( ServiceName.class ).getName();
}
public String testQualifiedServices()
{
String str = "";
for( ServiceReference<MyService> qualifiedServiceRef : qualifiedServiceRefs )
{
str += qualifiedServiceRef.metaInfo( ServiceName.class ).getName();
}
return str;
}
public T testStringIterable()
{
return stringService.get();
}
public Long testLongIterable()
{
return longService.get();
}
}
@Qualifier( NamedSelector.class )
@Retention( RetentionPolicy.RUNTIME )
public @interface Named
{
String value();
}
public static final class NamedSelector
implements AnnotationQualifier<Named>
{
public <T> Predicate<ServiceReference<?>> qualifier( final Named named )
{
return new Predicate<ServiceReference<?>>()
{
public boolean test( ServiceReference<?> service )
{
ServiceName serviceName = service.metaInfo( ServiceName.class );
return ( serviceName != null && serviceName.getName().equals( named.value() ) );
}
};
}
}
public static class ServiceName
implements Serializable
{
private String name;
public ServiceName( String name )
{
this.name = name;
}
public String getName()
{
return name;
}
}
public interface Foo<T>
{
T get();
}
@Mixins( StringService.Mixin.class )
public interface StringService
extends Foo<String>, ServiceComposite
{
class Mixin
implements Foo<String>
{
@Override
public String get()
{
return "A";
}
}
}
@Mixins( LongService.Mixin.class )
public interface LongService
extends Foo<Long>, ServiceComposite
{
class Mixin
implements Foo<Long>
{
@Override
public Long get()
{
return 1L;
}
}
}
}