├── lib └── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .bach ├── .gitignore └── src │ ├── Build.java │ ├── Document.java │ └── Rebuild.java ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── google-java-format.xml ├── libraries │ └── lib.xml ├── vcs.xml ├── misc.xml ├── se.jbee.lang.iml ├── .bach.run.bach.iml ├── runConfigurations │ └── All_tests_in_module_test_integration.xml ├── test.examples.iml ├── se.jbee.junit.assertion.iml ├── se.jbee.inject.bind.iml ├── se.jbee.inject.container.iml ├── se.jbee.inject.api.iml ├── se.jbee.inject.action.iml ├── se.jbee.inject.convert.iml ├── purejin.iml ├── se.jbee.inject.contract.iml ├── se.jbee.inject.event.iml ├── se.jbee.inject.bootstrap.iml ├── se.jbee.inject.iml ├── bach.project.iml ├── test.integration.iml └── modules.xml ├── .sdkmanrc ├── se.jbee.inject.event └── main │ └── java │ ├── se │ └── jbee │ │ └── inject │ │ ├── event │ │ ├── EventTarget.java │ │ ├── EventDispatcher.java │ │ ├── EventHandler.java │ │ ├── EventRecovery.java │ │ ├── DefaultEventDispatcher.java │ │ ├── Event.java │ │ ├── EventDispatch.java │ │ ├── On.java │ │ ├── EventTrigger.java │ │ └── EventModule.java │ │ ├── schedule │ │ ├── ScheduledRunDispatch.java │ │ ├── package-info.java │ │ ├── Schedule.java │ │ └── Scheduled.java │ │ └── disk │ │ └── DiskScopeModule.java │ └── module-info.java ├── test.examples └── test │ └── java │ ├── test │ ├── example2 │ │ ├── Bus.java │ │ ├── Car.java │ │ ├── bus │ │ │ ├── BusModule.java │ │ │ ├── BusService.java │ │ │ └── BusEnvModule.java │ │ ├── car │ │ │ ├── pool │ │ │ │ ├── corporate │ │ │ │ │ ├── CorporateCarPoolService.java │ │ │ │ │ └── CorporateCarPoolModule.java │ │ │ │ ├── CarPoolService.java │ │ │ │ ├── CarPoolModule.java │ │ │ │ └── CarPoolEnvModule.java │ │ │ ├── CarModule.java │ │ │ ├── CarService.java │ │ │ └── CarEnvModule.java │ │ ├── Example2Bundle.java │ │ └── Example2EnvBundle.java │ ├── example1 │ │ ├── Example1EnvBundle.java │ │ ├── Example1RootBundle.java │ │ ├── Support.java │ │ ├── Example1Module.java │ │ └── SupportAnnotationTemplet.java │ ├── Example.java │ ├── ExamplesEnvBundle.java │ └── ExamplesBundle.java │ └── module-info.java ├── test.integration └── test │ ├── resources │ └── junit-platform.properties │ └── java │ ├── test │ └── integration │ │ ├── util │ │ ├── WebMethod.java │ │ ├── Scoped.java │ │ ├── Resource.java │ │ └── TestUtils.java │ │ ├── bind │ │ ├── TouchServiceLoaderClasses.java │ │ ├── TestExampleLambdaBinds.java │ │ ├── TestBasicConstantBinds.java │ │ ├── TestBasicInstanceBinds.java │ │ ├── TestFeatureTypeDependentScopes.java │ │ ├── TestExampleDecoratorBinds.java │ │ ├── TestBasicInjectorExceptionsBinds.java │ │ ├── TestFeatureLiftOnlyWhenBinds.java │ │ ├── package-info.java │ │ ├── TestBasicLoggerBinds.java │ │ ├── TestBasicScopedBinds.java │ │ ├── TestExampleInterfaceDecouplingBinds.java │ │ ├── TestFeaturePackageLocalClassesBinds.java │ │ ├── TestFeatureLiftWithGenericsBinds.java │ │ ├── TestBasicSupplierBinds.java │ │ ├── TestFeatureInstallsAnnotationBinds.java │ │ ├── TestBasicMultiInstallBinds.java │ │ ├── TestExampleAnnotatedScopeBinds.java │ │ └── TestExampleDependencyAsHintBinds.java │ │ ├── api │ │ ├── TestTarget.java │ │ ├── TestCast.java │ │ └── TestResource.java │ │ ├── example0 │ │ └── TestServiceLoaderBootstrap.java │ │ ├── event │ │ └── RecordingScheduledExecutor.java │ │ ├── example1 │ │ ├── TestServiceLoaderBootstrapBinds.java │ │ └── TestCustomAnnotationBinds.java │ │ ├── convert │ │ ├── TestConvertTo.java │ │ └── TestCollectionConverter.java │ │ ├── container │ │ └── Decorate.java │ │ └── contract │ │ └── TestRoundRobinComputeEvents.java │ └── module-info.java ├── .gitmodules ├── se.jbee.inject.bind └── main │ └── java │ ├── se │ └── jbee │ │ └── inject │ │ ├── binder │ │ ├── spi │ │ │ ├── ConnectInBinder.java │ │ │ ├── package-info.java │ │ │ ├── ConfiguringInstanceLocalBinder.java │ │ │ ├── ReferenceBinder.java │ │ │ ├── PackageLocalBinder.java │ │ │ ├── ConnectorBinder.java │ │ │ ├── InstanceLocalBinder.java │ │ │ └── ParentLocalBinder.java │ │ ├── LocalEnvModule.java │ │ ├── ServiceLoaderEnvBundles.java │ │ ├── ServiceLoaderBundles.java │ │ ├── FilteredServiceLoaderBundles.java │ │ ├── SimpleModule.java │ │ ├── Constant.java │ │ ├── package-info.java │ │ ├── BundleFor.java │ │ └── BinderModuleWith.java │ │ ├── config │ │ ├── Extension.java │ │ ├── Feature.java │ │ ├── Connector.java │ │ ├── Get.java │ │ ├── Invoke.java │ │ ├── New.java │ │ ├── ScopesBy.java │ │ └── Edition.java │ │ └── bind │ │ ├── Bundle.java │ │ ├── Dependent.java │ │ ├── Module.java │ │ ├── BindingType.java │ │ └── ModuleWith.java │ └── module-info.java ├── se.jbee.inject.contract └── main │ └── java │ ├── module-info.java │ └── se │ └── jbee │ └── inject │ └── contract │ ├── package-info.java │ ├── PolicyProvider.java │ └── Event.java ├── se.jbee.inject.convert └── main │ └── java │ ├── se │ └── jbee │ │ └── inject │ │ └── convert │ │ ├── package-info.java │ │ ├── ConvertsMultiple.java │ │ ├── Imports.java │ │ ├── Converts.java │ │ └── ConverterModule.java │ └── module-info.java ├── se.jbee.inject.api └── main │ └── java │ ├── module-info.java │ └── se │ └── jbee │ └── inject │ ├── InjectionPoint.java │ ├── DisconnectException.java │ ├── Descriptor.java │ ├── package-info.java │ ├── Verifier.java │ ├── Injection.java │ ├── Converter.java │ ├── Annotated.java │ ├── Provider.java │ ├── InconsistentDeclaration.java │ ├── Generator.java │ ├── ContextAware.java │ └── Extends.java ├── se.jbee.inject.container └── main │ └── java │ ├── module-info.java │ └── se │ └── jbee │ └── inject │ └── container │ ├── package-info.java │ └── SupplyContext.java ├── se.jbee.lang └── main │ └── java │ ├── module-info.java │ └── se │ └── jbee │ └── lang │ ├── Typed.java │ ├── Lazy.java │ └── Qualifying.java ├── RELEASE_NOTES.md ├── se.jbee.inject.action └── main │ └── java │ ├── module-info.java │ └── se │ └── jbee │ └── inject │ └── action │ ├── ActionExecutionFailed.java │ ├── package-info.java │ ├── ActionExecutor.java │ ├── MulticastDispatch.java │ ├── RoundRobinDispatch.java │ ├── Action.java │ └── ActionDispatch.java ├── se.jbee.inject └── main │ └── java │ └── module-info.java ├── se.jbee.junit.assertion └── test │ └── java │ ├── module-info.java │ └── se │ └── jbee │ └── junit │ └── assertion │ ├── AssertionUtils.java │ └── Await.java ├── se.jbee.inject.bootstrap └── main │ └── java │ ├── se │ └── jbee │ │ └── inject │ │ ├── defaults │ │ ├── DefaultsBundle.java │ │ ├── DefaultFeatures.java │ │ ├── ExtensionModule.java │ │ └── package-info.java │ │ ├── bootstrap │ │ ├── ModuleBootstrapper.java │ │ ├── BundleBootstrapper.java │ │ └── package-info.java │ │ └── scope │ │ ├── ThreadScope.java │ │ ├── ApplicationScope.java │ │ ├── SnapshotScope.java │ │ └── package-info.java │ └── module-info.java └── README /lib/.gitignore: -------------------------------------------------------------------------------- 1 | *.jar 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jbee 2 | -------------------------------------------------------------------------------- /.bach/.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | var/ 3 | 4 | *.class 5 | *.jar 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | target/ 3 | src/doc/ 4 | pom.* 5 | *~ 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | shelf/ 3 | uiDesigner.xml 4 | workspace.xml 5 | sonarlint/ 6 | -------------------------------------------------------------------------------- /.sdkmanrc: -------------------------------------------------------------------------------- 1 | # Enable auto-env through the sdkman_auto_env config 2 | # Add key=value pairs of SDKs to use below 3 | java=18.0.1.1-open 4 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventTarget.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | public final class EventTarget { 4 | } 5 | -------------------------------------------------------------------------------- /.bach/src/Build.java: -------------------------------------------------------------------------------- 1 | public class Build { 2 | public static void main(String... args) { 3 | Project.ofCurrentWorkingDirectory().build(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.bach/src/Document.java: -------------------------------------------------------------------------------- 1 | class Document { 2 | public static void main(String... args) { 3 | Project.ofCurrentWorkingDirectory().document(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.bach/src/Rebuild.java: -------------------------------------------------------------------------------- 1 | class Rebuild { 2 | public static void main(String... args) { 3 | var project = Project.ofCurrentWorkingDirectory(); 4 | project.clean(); 5 | project.build(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/Bus.java: -------------------------------------------------------------------------------- 1 | package test.example2; 2 | 3 | public class Bus { 4 | 5 | public final String to; 6 | 7 | public Bus(String to) { 8 | this.to = to; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.idea/google-java-format.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/Car.java: -------------------------------------------------------------------------------- 1 | package test.example2; 2 | 3 | public class Car { 4 | 5 | public final String owner; 6 | 7 | public Car(String owner) { 8 | this.owner = owner; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventDispatcher.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | public interface EventDispatcher { 4 | 5 | void handle(Event event, EventHandler handler); 6 | } 7 | -------------------------------------------------------------------------------- /test.integration/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.execution.parallel.enabled = true 2 | #junit.jupiter.execution.parallel.mode.default = same_thread 3 | junit.jupiter.execution.parallel.mode.default = concurrent 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".bach/src/run/bach"] 2 | path = .bach/src/run/bach 3 | url = https://github.com/sormuras/run.bach 4 | [submodule "junit"] 5 | path = .bach/src/run/info/org/junit 6 | url = https://github.com/junit-team/bach-info 7 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/ConnectInBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.config.ProducesBy; 4 | 5 | public interface ConnectInBinder { 6 | 7 | B connect(ProducesBy connectsBy); 8 | } 9 | -------------------------------------------------------------------------------- /se.jbee.inject.contract/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module se.jbee.inject.contract { 2 | 3 | requires transitive se.jbee.lang; 4 | requires transitive se.jbee.inject.api; 5 | requires transitive se.jbee.inject.bind; 6 | 7 | exports se.jbee.inject.contract; 8 | } 9 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventHandler.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | import java.util.List; 4 | 5 | public final class EventHandler { 6 | 7 | public List handle(Event event) { 8 | //TODO 9 | return null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /se.jbee.inject.convert/main/java/se/jbee/inject/convert/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains an add-on with the experimental implementation of a 3 | * fully generic, integrated type {@link se.jbee.inject.Converter} facility. 4 | *

5 | */ 6 | package se.jbee.inject.convert; 7 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/bus/BusModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.bus; 2 | 3 | import se.jbee.inject.binder.BinderModule; 4 | 5 | public class BusModule extends BinderModule { 6 | @Override 7 | protected void declare() { 8 | autobind().in(BusService.class); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example1/Example1EnvBundle.java: -------------------------------------------------------------------------------- 1 | package test.example1; 2 | 3 | import se.jbee.inject.binder.EnvModule; 4 | 5 | public class Example1EnvBundle extends EnvModule { 6 | 7 | @Override 8 | protected void declare() { 9 | bind(Integer.class).to(13); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the core API of the library. Most prominently the {@link 3 | * se.jbee.inject.Injector} context abstraction. 4 | */ 5 | module se.jbee.inject.api { 6 | 7 | requires transitive se.jbee.lang; 8 | 9 | exports se.jbee.inject; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.idea/libraries/lib.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/pool/corporate/CorporateCarPoolService.java: -------------------------------------------------------------------------------- 1 | package test.example2.car.pool.corporate; 2 | 3 | import test.example2.Car; 4 | 5 | public class CorporateCarPoolService { 6 | 7 | public Car[] corporatePool() { 8 | return new Car[] { new Car("monopoly1") }; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /se.jbee.inject.container/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the {@link se.jbee.inject.Injector} context implementation. 3 | */ 4 | module se.jbee.inject.container { 5 | 6 | requires transitive se.jbee.lang; 7 | requires transitive se.jbee.inject.api; 8 | 9 | exports se.jbee.inject.container; 10 | } 11 | -------------------------------------------------------------------------------- /se.jbee.lang/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | import se.jbee.lang.Type; 2 | 3 | /** 4 | * Contains Java language and JDK level utility classes. Most importantly {@link 5 | * Type} modelling fully generic types without a tree of 6 | * subtypes. 7 | */ 8 | module se.jbee.lang { 9 | 10 | exports se.jbee.lang; 11 | } 12 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example1/Example1RootBundle.java: -------------------------------------------------------------------------------- 1 | package test.example1; 2 | 3 | import se.jbee.inject.binder.BootstrapperBundle; 4 | 5 | public class Example1RootBundle extends BootstrapperBundle { 6 | 7 | @Override 8 | protected void bootstrap() { 9 | install(Example1Module.class); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventRecovery.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | @FunctionalInterface 4 | public interface EventRecovery { 5 | 6 | interface Controller { 7 | 8 | } 9 | 10 | void recover(Exception ex, Event event, EventHandler handler, Controller controller); 11 | } 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | ## 8.1 4 | 5 | Is the first major release as _purejin_ dependency injection library. 6 | 7 | Assets can be found at https://github.com/jbee/purejin/releases/tag/8.1 8 | 9 | **NOTE:** The `convert`, `event` and `contract` add-on modules are _work in progress_ 10 | and not included in the release. 11 | 12 | 13 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/pool/corporate/CorporateCarPoolModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.car.pool.corporate; 2 | 3 | import se.jbee.inject.binder.BinderModule; 4 | 5 | public class CorporateCarPoolModule extends BinderModule { 6 | 7 | @Override 8 | protected void declare() { 9 | autobind().in(CorporateCarPoolService.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example1/Support.java: -------------------------------------------------------------------------------- 1 | package test.example1; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.TYPE; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Target(TYPE) 10 | public @Retention(RUNTIME) @interface Support { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /se.jbee.inject.convert/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains an add-on for convenient type conversion based on the {@link 3 | * se.jbee.inject.Converter} abstraction. 4 | */ 5 | module se.jbee.inject.convert { 6 | 7 | requires transitive se.jbee.lang; 8 | requires transitive se.jbee.inject.api; 9 | requires transitive se.jbee.inject.bind; 10 | 11 | exports se.jbee.inject.convert; 12 | } 13 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/util/WebMethod.java: -------------------------------------------------------------------------------- 1 | package test.integration.util; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.METHOD; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | @Target(METHOD) 11 | public @interface WebMethod { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/DefaultEventDispatcher.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | import se.jbee.inject.config.Connector; 4 | import se.jbee.lang.Type; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | public class DefaultEventDispatcher implements Connector { 9 | 10 | @Override 11 | public void connect(Object instance, Type as, Method connected) { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/Example2Bundle.java: -------------------------------------------------------------------------------- 1 | package test.example2; 2 | 3 | import se.jbee.inject.binder.BootstrapperBundle; 4 | import test.example2.bus.BusModule; 5 | import test.example2.car.CarModule; 6 | 7 | public class Example2Bundle extends BootstrapperBundle { 8 | 9 | @Override 10 | protected void bootstrap() { 11 | install(CarModule.class); 12 | install(BusModule.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/util/Scoped.java: -------------------------------------------------------------------------------- 1 | package test.integration.util; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.TYPE; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | @Target(TYPE) 11 | public @interface Scoped { 12 | 13 | String value(); 14 | } 15 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains an add-on to share and call vanilla Java methods as {@link 3 | * se.jbee.inject.action.Action}s to decouple caller and called code. 4 | */ 5 | module se.jbee.inject.action { 6 | 7 | requires transitive se.jbee.lang; 8 | requires transitive se.jbee.inject.api; 9 | requires transitive se.jbee.inject.bind; 10 | 11 | exports se.jbee.inject.action; 12 | } 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/InjectionPoint.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | 5 | public interface InjectionPoint extends Annotated { 6 | 7 | InjectionPoint NONE = () -> Annotated.NOT_ANNOTATED; 8 | 9 | static InjectionPoint at(AnnotatedElement param) { 10 | return () -> param; 11 | } 12 | 13 | default boolean isNone() { 14 | return this == NONE; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/CarModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.car; 2 | 3 | import se.jbee.inject.binder.BinderModule; 4 | import se.jbee.inject.binder.Installs; 5 | import test.example2.car.pool.CarPoolModule; 6 | 7 | @Installs(bundles = CarPoolModule.class) 8 | public class CarModule extends BinderModule { 9 | 10 | @Override 11 | protected void declare() { 12 | autobind().in(CarService.class); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/CarService.java: -------------------------------------------------------------------------------- 1 | package test.example2.car; 2 | 3 | import test.example2.Bus; 4 | import test.example2.Car; 5 | 6 | public class CarService { 7 | 8 | public Car getPetersCar() { 9 | return new Car("Peter"); 10 | } 11 | 12 | public Car getPaulsCar() { 13 | return new Car("Paul"); 14 | } 15 | 16 | public Bus busStopAtCarPark() { 17 | return new Bus("at-car-park"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /se.jbee.inject/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * A BOM type module that references the core modules of the library 3 | */ 4 | @SuppressWarnings("Java9RedundantRequiresStatement") 5 | module se.jbee.inject { 6 | 7 | requires transitive se.jbee.lang; 8 | requires transitive se.jbee.inject.api; 9 | requires transitive se.jbee.inject.bind; 10 | requires transitive se.jbee.inject.container; 11 | requires transitive se.jbee.inject.bootstrap; 12 | } 13 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/util/Resource.java: -------------------------------------------------------------------------------- 1 | package test.integration.util; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.*; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | @Target({ METHOD, PARAMETER, FIELD }) 11 | public @interface Resource { 12 | 13 | String value() default ""; 14 | } 15 | -------------------------------------------------------------------------------- /se.jbee.junit.assertion/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | module se.jbee.junit.assertion { 2 | 3 | requires org.junit.jupiter.api; 4 | requires org.junit.platform.engine; 5 | requires org.junit.platform.launcher; 6 | 7 | exports se.jbee.junit.assertion; 8 | 9 | opens se.jbee.junit.assertion to org.junit.platform.commons; 10 | 11 | provides org.junit.platform.launcher.TestExecutionListener with 12 | se.jbee.junit.assertion.listener.ContainerFeed; 13 | } 14 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/pool/CarPoolService.java: -------------------------------------------------------------------------------- 1 | package test.example2.car.pool; 2 | 3 | import test.example2.Bus; 4 | import test.example2.Car; 5 | 6 | public class CarPoolService { 7 | 8 | public Car[] poolCars() { 9 | return new Car[] { new Car("pool1"), new Car("pool2") }; 10 | } 11 | 12 | public Car nextPoolCar() { 13 | return null; 14 | } 15 | 16 | public Bus stopAtCarPool() { 17 | return new Bus("stop-at-car-pool"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/pool/CarPoolModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.car.pool; 2 | 3 | import se.jbee.inject.binder.BinderModule; 4 | import se.jbee.inject.binder.Installs; 5 | import test.example2.car.pool.corporate.CorporateCarPoolModule; 6 | 7 | @Installs(bundles = CorporateCarPoolModule.class) 8 | public class CarPoolModule extends BinderModule { 9 | 10 | @Override 11 | protected void declare() { 12 | autobind().in(CarPoolService.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example1/Example1Module.java: -------------------------------------------------------------------------------- 1 | package test.example1; 2 | 3 | import se.jbee.inject.binder.BinderModuleWith; 4 | 5 | public class Example1Module extends BinderModuleWith { 6 | 7 | @Override 8 | protected void declare(Integer value) { 9 | // the value is defined in the Env, in this case it is from 10 | // MyEnvSetupBundle which sets it to 13 11 | bind(int.class).to(value); 12 | bind(String.class).to(getClass().getName()); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/ActionExecutionFailed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.action; 7 | 8 | public class ActionExecutionFailed extends RuntimeException { 9 | 10 | public ActionExecutionFailed(String message, Throwable cause) { 11 | super(message + ": " + cause.getMessage(), cause); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /se.jbee.inject.convert/main/java/se/jbee/inject/convert/ConvertsMultiple.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.convert; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 | 10 | @Documented 11 | @Retention(RUNTIME) 12 | @Target(ElementType.TYPE) 13 | public @interface ConvertsMultiple { 14 | Converts[] value(); 15 | } 16 | -------------------------------------------------------------------------------- /.idea/se.jbee.lang.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test.examples/test/java/test/Example.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import se.jbee.inject.Env; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.bootstrap.Bootstrap; 6 | 7 | public enum Example { 8 | 9 | EXAMPLE_1, 10 | EXAMPLE_2; 11 | 12 | public Env env() { 13 | return Bootstrap.env( 14 | Bootstrap.DEFAULT_ENV.withDependent(Example.class, this)); 15 | } 16 | 17 | public Injector injector() { 18 | return Bootstrap.injector(env().withDependent(Example.class, this)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/pool/CarPoolEnvModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.car.pool; 2 | 3 | import se.jbee.inject.binder.LocalEnvModule; 4 | import se.jbee.inject.config.ProducesBy; 5 | import test.example2.Car; 6 | 7 | import static se.jbee.lang.Type.raw; 8 | 9 | public class CarPoolEnvModule extends LocalEnvModule { 10 | @Override 11 | protected void declare() { 12 | bind(ProducesBy.class).to(ProducesBy.declaredMethods(false) 13 | .returnTypeAssignableTo(raw(Car[].class))); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/Example2EnvBundle.java: -------------------------------------------------------------------------------- 1 | package test.example2; 2 | 3 | import se.jbee.inject.Env; 4 | import se.jbee.inject.Extends; 5 | import se.jbee.inject.binder.BootstrapperBundle; 6 | import test.example2.bus.BusEnvModule; 7 | import test.example2.car.CarEnvModule; 8 | 9 | @Extends(Env.class) 10 | public class Example2EnvBundle extends BootstrapperBundle { 11 | 12 | @Override 13 | protected void bootstrap() { 14 | install(CarEnvModule.class); 15 | install(BusEnvModule.class); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/bus/BusService.java: -------------------------------------------------------------------------------- 1 | package test.example2.bus; 2 | 3 | import test.example2.Bus; 4 | import test.example2.Car; 5 | 6 | public class BusService { 7 | 8 | public Bus toLondon() { 9 | return new Bus("London"); 10 | } 11 | 12 | public Bus toGlasgow() { 13 | return new Bus("Glasgow"); 14 | } 15 | 16 | /** 17 | * This should not be picked as it does not return a {@link Bus} 18 | */ 19 | public Car carOfBusDriver() { 20 | return new Car("bus-driver-to-London"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains an add-on for an experimental asynchronous event 3 | * dispatch on vanilla java interfaces and methods. 4 | */ 5 | module se.jbee.inject.event { 6 | 7 | requires transitive se.jbee.lang; 8 | requires transitive se.jbee.inject.api; 9 | requires transitive se.jbee.inject.bind; 10 | requires se.jbee.inject.bootstrap; 11 | 12 | exports se.jbee.inject.event; 13 | exports se.jbee.inject.schedule; 14 | exports se.jbee.inject.disk; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /.idea/.bach.run.bach.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/Event.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | public final class Event { 4 | 5 | public final T type; 6 | 7 | public Event(T type) { 8 | this.type = type; 9 | } 10 | 11 | //TODO add parent Event and a event loop detection 12 | // similar to dependencies each time an event handler triggers new events those become 13 | // children of that event just that in case of events the children refer to their parent 14 | // initial event (from a trigger) do not have a parent event 15 | } 16 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TouchServiceLoaderClasses.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.binder.ServiceLoaderAnnotations; 5 | import se.jbee.inject.binder.ServiceLoaderBundles; 6 | import se.jbee.inject.binder.ServiceLoaderEnvBundles; 7 | 8 | class TouchServiceLoaderClasses { 9 | 10 | @SuppressWarnings("unused") 11 | @Test 12 | void test() { 13 | new ServiceLoaderAnnotations(); 14 | new ServiceLoaderBundles(); 15 | new ServiceLoaderEnvBundles(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/Extension.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.config; 2 | 3 | import se.jbee.inject.Injector; 4 | import se.jbee.lang.Utils; 5 | 6 | /** 7 | * Marker interface to mark classes representing an {@link Injector} API 8 | * {@link Extension}. 9 | * 10 | * This means the marked type does not need to be bound explicitly. A singleton 11 | * instance per type is created using the type's 12 | * {@link Utils#mostVisibleMostParametersConstructor(Class)}. 13 | * 14 | * @since 8.1 15 | */ 16 | public interface Extension { 17 | // marker 18 | } 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/All_tests_in_module_test_integration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | -------------------------------------------------------------------------------- /.idea/test.examples.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/bus/BusEnvModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.bus; 2 | 3 | import se.jbee.inject.binder.LocalEnvModule; 4 | import se.jbee.inject.config.NamesBy; 5 | import se.jbee.inject.config.ProducesBy; 6 | import test.example2.Bus; 7 | 8 | import static se.jbee.lang.Type.raw; 9 | 10 | public class BusEnvModule extends LocalEnvModule { 11 | 12 | @Override 13 | protected void declare() { 14 | bind(NamesBy.class).to(NamesBy.DECLARED_NAME); 15 | bind(ProducesBy.class).to(ProducesBy.declaredMethods(false) 16 | .returnTypeAssignableTo(raw(Bus.class))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /se.jbee.junit.assertion/test/java/se/jbee/junit/assertion/AssertionUtils.java: -------------------------------------------------------------------------------- 1 | package se.jbee.junit.assertion; 2 | 3 | final class AssertionUtils { 4 | 5 | private AssertionUtils() { 6 | throw new UnsupportedOperationException("util"); 7 | } 8 | 9 | static String buildPrefix(String message) { 10 | return message != null && !message.trim().isEmpty() ? message + " ==> " : ""; 11 | } 12 | 13 | static > String formatExpectedButWas( 14 | String op, T expected, T butWas) { 15 | return String.format("expected: %s <%s> but was: <%s>", // 16 | op, expected, butWas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/DisconnectException.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | /** 4 | * A {@link DisconnectException} is thrown by {@link java.lang.reflect.Method}s 5 | * (that assume to be called as a consequence of being connected by a {@code 6 | * Connector}) in case they want to undo the connection and should no longer be 7 | * called. 8 | */ 9 | public class DisconnectException extends RuntimeException { 10 | 11 | public DisconnectException(String msg, Throwable cause) { 12 | super(msg, cause); 13 | } 14 | public DisconnectException(String msg) { 15 | super(msg); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.idea/se.jbee.junit.assertion.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test.integration/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module test.integration { 2 | 3 | requires java.logging; 4 | requires org.junit.jupiter; 5 | requires static org.junit.platform.console; // <- launches test modules 6 | requires static org.junit.platform.jfr; // <- flight-recording support 7 | 8 | requires se.jbee.junit.assertion; 9 | 10 | /* core */ 11 | requires transitive se.jbee.inject; 12 | /* and the add-ons */ 13 | requires se.jbee.inject.convert; 14 | requires se.jbee.inject.action; 15 | requires se.jbee.inject.event; 16 | requires se.jbee.inject.contract; 17 | 18 | requires test.examples; // <- module under test 19 | } 20 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.bind.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/schedule/ScheduledRunDispatch.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.schedule; 2 | 3 | import se.jbee.inject.event.*; 4 | 5 | import java.util.Map; 6 | 7 | public class ScheduledRunDispatch 8 | implements EventDispatch { 9 | 10 | @Override 11 | public void dispatch(Event event, 12 | Map> handlers, 13 | EventDispatcher dispatcher) { 14 | EventHandler receiver = handlers.get(event.type.target); 15 | if (receiver == null) { 16 | //TODO 17 | return; 18 | } 19 | dispatcher.handle(event, receiver); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.container.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/util/TestUtils.java: -------------------------------------------------------------------------------- 1 | package test.integration.util; 2 | 3 | @Deprecated(forRemoval = true) // use Assertions 4 | public final class TestUtils { 5 | 6 | private TestUtils() { 7 | throw new UnsupportedOperationException("util"); 8 | } 9 | 10 | public static boolean wait50() { 11 | return await(50); 12 | } 13 | 14 | public static boolean wait20() { 15 | return await(20); 16 | } 17 | 18 | @SuppressWarnings("squid:S2925") 19 | private static boolean await(long millis) { 20 | try { 21 | Thread.sleep(millis); 22 | return true; 23 | } catch (InterruptedException e) { 24 | return false; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/LocalEnvModule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder; 2 | 3 | import se.jbee.inject.bind.Bind; 4 | 5 | /** 6 | * A {@link EnvModule} where all made binds (by default) only apply to the 7 | * package of the extending class and all its sub-packages. 8 | * 9 | * This merely exists for clarity and convenience and as a way to learn how 10 | * to create a package localised effect. 11 | */ 12 | public abstract class LocalEnvModule extends EnvModule { 13 | 14 | @Override 15 | protected Bind init(Bind bind) { 16 | Bind res = super.init(bind); 17 | return res.with(res.target.inPackageAndSubPackagesOf(getClass())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test.examples/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | import test.ExamplesBundle; 2 | import test.ExamplesEnvBundle; 3 | import test.example1.SupportAnnotationTemplet; 4 | 5 | /** 6 | * Contains an example application used in tests to test the {@link 7 | * java.util.ServiceLoader} based features. 8 | */ 9 | @SuppressWarnings("Java9RedundantRequiresStatement") 10 | open module test.examples { 11 | 12 | exports test; 13 | exports test.example1; 14 | exports test.example2; 15 | 16 | requires transitive se.jbee.inject; 17 | 18 | provides se.jbee.inject.bind.Bundle with ExamplesBundle, ExamplesEnvBundle; 19 | 20 | provides se.jbee.inject.bind.ModuleWith with SupportAnnotationTemplet; 21 | } 22 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the fluent {@link se.jbee.inject.binder.Binder} API used by software 3 | * modules to issue their bindings in a convenient way. 4 | *

5 | * In multi-module application this is usually required by each module to bind 6 | * the services provided by the software module. 7 | */ 8 | module se.jbee.inject.bind { 9 | 10 | requires transitive se.jbee.lang; 11 | requires transitive se.jbee.inject.api; 12 | 13 | exports se.jbee.inject.bind; 14 | exports se.jbee.inject.binder; 15 | exports se.jbee.inject.binder.spi; 16 | exports se.jbee.inject.config; 17 | 18 | uses se.jbee.inject.bind.Bundle; 19 | uses se.jbee.inject.bind.ModuleWith; 20 | } 21 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example2/car/CarEnvModule.java: -------------------------------------------------------------------------------- 1 | package test.example2.car; 2 | 3 | import se.jbee.inject.binder.Installs; 4 | import se.jbee.inject.binder.LocalEnvModule; 5 | import se.jbee.inject.config.NamesBy; 6 | import se.jbee.inject.config.ProducesBy; 7 | import se.jbee.lang.Type; 8 | import test.example2.Car; 9 | import test.example2.car.pool.CarPoolEnvModule; 10 | 11 | @Installs(bundles = CarPoolEnvModule.class) 12 | public class CarEnvModule extends LocalEnvModule { 13 | 14 | @Override 15 | protected void declare() { 16 | bind(NamesBy.class).to(NamesBy.DECLARED_NAME); 17 | bind(ProducesBy.class).to(ProducesBy.declaredMethods(false) 18 | .returnTypeAssignableTo(Type.raw(Car.class))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.action.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.convert.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/purejin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.contract.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/bind/Bundle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bind; 7 | 8 | /** 9 | * A bundle installs sub-bundles and {@link Module}s. 10 | * 11 | * All {@link Bundle}s are real singletons. A bundle means you get X all the 12 | * time. No less, no more. Everything (installed) or nothing (uninstalled). 13 | */ 14 | @FunctionalInterface 15 | public interface Bundle { 16 | 17 | /** 18 | * @param bootstrap The {@link Bootstrapper} this {@link Bundle} should 19 | * install itself to. 20 | */ 21 | void bootstrap(Bootstrapper bootstrap); 22 | } 23 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventDispatch.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * The {@link EventDispatch} calls zero, one or more {@link 7 | * EventHandler}s that according to their strategy should handle the dispatched 8 | * {@link Event}. 9 | * 10 | * @param type of the raw event dispatched 11 | */ 12 | @FunctionalInterface 13 | public interface EventDispatch { 14 | 15 | /** 16 | * Selects the {@link EventHandler}(s) to use to {@link 17 | * EventHandler#handle(Event)} the event. 18 | */ 19 | void dispatch(Event event, Map> handlers, 20 | EventDispatcher dispatcher); 21 | 22 | //NOTE: fallback is part of dispatch 23 | } 24 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/defaults/DefaultsBundle.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.defaults; 2 | 3 | import se.jbee.inject.Injector; 4 | import se.jbee.inject.Resource; 5 | import se.jbee.inject.bind.Bundle; 6 | import se.jbee.inject.bind.Module; 7 | import se.jbee.inject.binder.BootstrapperBundle; 8 | 9 | /** 10 | * A {@link Bundle} that installs all {@link DefaultFeature} features active by 11 | * default. This means a {@link Injector} feature is implemented by providing a 12 | * {@link Resource} which is bound as a usual {@link Module}. 13 | * 14 | * @since 8.1 15 | */ 16 | public final class DefaultsBundle extends BootstrapperBundle { 17 | 18 | @Override 19 | protected void bootstrap() { 20 | install(DefaultFeature.INSTALLED_BY_DEFAULT); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test.examples/test/java/test/ExamplesEnvBundle.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import se.jbee.inject.Env; 4 | import se.jbee.inject.Extends; 5 | import se.jbee.inject.bind.Bootstrapper; 6 | import se.jbee.inject.bind.Bundle; 7 | import se.jbee.inject.binder.BundleFor; 8 | import test.example1.Example1EnvBundle; 9 | import test.example2.Example2EnvBundle; 10 | 11 | @Extends(Env.class) 12 | public class ExamplesEnvBundle extends BundleFor implements Bundle { 13 | 14 | @Override 15 | protected void bootstrap() { 16 | installDependentOn(Example.EXAMPLE_1, Example1EnvBundle.class); 17 | installDependentOn(Example.EXAMPLE_2, Example2EnvBundle.class); 18 | } 19 | 20 | @Override 21 | public void bootstrap(Bootstrapper bootstrap) { 22 | bootstrap.install(getClass(), Example.class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.event.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/ServiceLoaderEnvBundles.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder; 2 | 3 | import se.jbee.inject.Env; 4 | import se.jbee.inject.Extends; 5 | import se.jbee.inject.bind.Bundle; 6 | 7 | /** 8 | * A {@link Bundle} that installs all {@link Bundle}s declared via {@link 9 | * java.util.ServiceLoader} which also got annotated with {@link Extends} 10 | * referring to {@link Env}. 11 | * 12 | * @since 8.1 13 | */ 14 | public class ServiceLoaderEnvBundles extends FilteredServiceLoaderBundles { 15 | 16 | @Override 17 | boolean bootstrap(Class bundle) { 18 | if (!bundle.isAnnotationPresent(Extends.class)) 19 | return false; 20 | Class target = bundle.getAnnotation(Extends.class).value(); 21 | return target == Env.class; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/bootstrap/ModuleBootstrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bootstrap; 7 | 8 | import se.jbee.inject.bind.Bundle; 9 | import se.jbee.inject.bind.Module; 10 | 11 | /** 12 | * Determines / extracts the {@link Module}s from a root {@link Bundle}. 13 | * 14 | * This is more a utility API for testing. 15 | */ 16 | @FunctionalInterface 17 | public interface ModuleBootstrapper { 18 | 19 | /** 20 | * @return All {@link Module}s that result from expanding the given root 21 | * {@link Bundle} to the module level. 22 | */ 23 | Module[] installedModules(Class root); 24 | } 25 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/defaults/DefaultFeatures.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.defaults; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.TYPE; 9 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 10 | 11 | /** 12 | * To be used in combination with {@link se.jbee.inject.binder.Installs} to 13 | * specify a set of {@link DefaultFeature}s that should be installed. 14 | */ 15 | @Inherited 16 | @Documented 17 | @Target(TYPE) 18 | @Retention(RUNTIME) 19 | public @interface DefaultFeatures { 20 | 21 | /** 22 | * @return the set of features to install 23 | */ 24 | DefaultFeature[] value(); 25 | } 26 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.bootstrap.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /se.jbee.lang/main/java/se/jbee/lang/Typed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.lang; 7 | 8 | /** 9 | * Has a {@link Type}, {@link #typed(Type)} as... 10 | * 11 | * @author Jan Bernitt (jan@jbee.se) 12 | * 13 | * @param The actual type ({@link Class}) 14 | */ 15 | @SuppressWarnings("ClassReferencesSubclass") 16 | public interface Typed { 17 | 18 | /** 19 | * @return The {@link Type} of this object. 20 | */ 21 | Type type(); 22 | 23 | /** 24 | * @return This object with the given {@link Type}. 25 | * 26 | * @throws ClassCastException in case this cannot be typed as the type 27 | * given. 28 | */ 29 | Typed typed(Type type); 30 | } 31 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the defaults for {@link se.jbee.inject.Scope} and {@link 3 | * se.jbee.inject.bind.ValueBinder}s required to {@link 4 | * se.jbee.inject.bootstrap.Bootstrap} a {@link se.jbee.inject.Injector} from 5 | * {@link se.jbee.inject.bind.Binding}s. 6 | *

7 | * In multi-module application this module should only be required in the single 8 | * module responsible for bootstrapping the application. 9 | */ 10 | module se.jbee.inject.bootstrap { 11 | 12 | requires java.logging; 13 | 14 | requires transitive se.jbee.lang; 15 | requires transitive se.jbee.inject.api; 16 | requires transitive se.jbee.inject.bind; 17 | requires se.jbee.inject.container; 18 | 19 | exports se.jbee.inject.bootstrap; 20 | exports se.jbee.inject.defaults; 21 | exports se.jbee.inject.scope; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/api/TestTarget.java: -------------------------------------------------------------------------------- 1 | package test.integration.api; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Dependency; 5 | import se.jbee.inject.Target; 6 | 7 | import java.util.List; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertFalse; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | import static se.jbee.inject.Dependency.dependency; 12 | import static se.jbee.inject.Target.targeting; 13 | 14 | class TestTarget { 15 | 16 | @Test 17 | void thatTargetInstancesNeedsToBeMatchedByDependencies() { 18 | Target target = targeting(List.class); 19 | Dependency dependency = dependency(String.class); 20 | assertFalse(target.isUsableFor(dependency)); 21 | assertTrue( 22 | target.isUsablePackageWise(dependency.injectingInto(List.class))); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/ServiceLoaderBundles.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder; 2 | 3 | import se.jbee.inject.Extends; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.bind.Bundle; 6 | 7 | /** 8 | * A {@link Bundle} that installs all {@link Bundle}s provided via {@link 9 | * java.util.ServiceLoader} which are not annotated with {@link Extends} or with 10 | * are annotated with {@link Extends} referring to {@link Injector} type. 11 | * 12 | * @since 8.1 13 | */ 14 | public class ServiceLoaderBundles extends FilteredServiceLoaderBundles { 15 | 16 | @Override 17 | boolean bootstrap(Class bundle) { 18 | if (!bundle.isAnnotationPresent(Extends.class)) 19 | return true; 20 | Class target = bundle.getAnnotation(Extends.class).value(); 21 | return target == Injector.class; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package contains a number of interfaces that are used to build fluent 3 | * APIs to make bindings. 4 | *

5 | * Each of the interfaces is a {@link java.lang.FunctionalInterface} that based 6 | * on on essential abstract method adds further {@code default} methods that 7 | * operate on the essential method. In most cases they provide an alternative 8 | * parameter list to allow use of different arguments in a convenient way. This 9 | * convenience "layer" can be added to API implementation by implementing the 10 | * interface. 11 | *

12 | * All interfaces have a type parameter for the binder type that is returned by 13 | * all of their methods so that implementations can return their specific binder 14 | * types. 15 | */ 16 | package se.jbee.inject.binder.spi; 17 | -------------------------------------------------------------------------------- /test.examples/test/java/test/ExamplesBundle.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import se.jbee.inject.bind.Bootstrapper; 4 | import se.jbee.inject.bind.Bundle; 5 | import se.jbee.inject.binder.BundleFor; 6 | import se.jbee.inject.defaults.DefaultFeature; 7 | import test.example1.Example1RootBundle; 8 | import test.example2.Example2Bundle; 9 | 10 | public class ExamplesBundle extends BundleFor implements Bundle { 11 | @Override 12 | protected void bootstrap() { 13 | installDependentOn(Example.EXAMPLE_1, Example1RootBundle.class); 14 | installDependentOn(Example.EXAMPLE_2, Example2Bundle.class); 15 | } 16 | 17 | @Override 18 | public void bootstrap(Bootstrapper bootstrap) { 19 | bootstrap.install(getClass(), Example.class); 20 | // just so that even without any example we have the env available 21 | bootstrap.install(DefaultFeature.ENV); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/bootstrap/BundleBootstrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bootstrap; 7 | 8 | import se.jbee.inject.bind.Bundle; 9 | 10 | /** 11 | * Determines all reachable {@link Bundle}s starting from a root {@link Bundle}. 12 | * 13 | * This is more a utility API for testing. 14 | */ 15 | @FunctionalInterface 16 | public interface BundleBootstrapper { 17 | 18 | /** 19 | * @param root origin of reachable computation 20 | * @return All {@link Bundle}s (their {@link Class}es) that are reachable 21 | * (installed) when starting from the given root {@link Bundle}. 22 | */ 23 | Class[] installedBundles(Class root); 24 | } 25 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/api/TestCast.java: -------------------------------------------------------------------------------- 1 | package test.integration.api; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.lang.Cast; 5 | import se.jbee.lang.Type; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | 9 | class TestCast { 10 | 11 | @Test 12 | void thatReturnTypeConformsToModelledType() { 13 | Type> listString = Cast.listTypeOf( 14 | String.class); 15 | assertEquals("java.util.List", listString.toString()); 16 | } 17 | 18 | @Test 19 | void thatReturnTypeConformsToNestedModelledType() { 20 | Type>> listListString = Cast.listTypeOf( 21 | Cast.listTypeOf(String.class)); 22 | assertEquals("java.util.List>", 23 | listListString.toString()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/bind/Dependent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bind; 7 | 8 | import se.jbee.inject.Env; 9 | import se.jbee.inject.bind.Bootstrapper.DependentBootstrapper; 10 | 11 | /** 12 | * A {@link Bundle} that does installs {@link Bundle}s in connected to a feature 13 | * flag represented by an {@link Enum} constant. If the flag is 14 | * {@link Env#isInstalled(Class, Enum)} the {@link Bundle} is installed, 15 | * otherwise it isn't. 16 | */ 17 | @FunctionalInterface 18 | public interface Dependent { 19 | 20 | /** 21 | * @param bootstrapper the {@link DependentBootstrapper} this bundle should 22 | * install itself in. 23 | */ 24 | void bootstrap(DependentBootstrapper bootstrapper); 25 | } 26 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/Feature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.config; 7 | 8 | /** 9 | * {@link Feature}s can be used to model more fine grained {@link Edition} by 10 | * using enums as the options to chose from. 11 | * 12 | * @author Jan Bernitt (jan@jbee.se) 13 | * 14 | * @param The enum used as different features/options to chose from. 15 | */ 16 | @FunctionalInterface 17 | public interface Feature> { 18 | 19 | /** 20 | * @return The feature this given bundle or module class represents or 21 | * null is it doesn't represent any special feature (so 22 | * it will be install in any case). 23 | */ 24 | T featureOf(Class bundleOrModule); 25 | } 26 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/bind/Module.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bind; 7 | 8 | import se.jbee.inject.Env; 9 | 10 | /** 11 | * {@link Bindings} are defined with {@link Module}s while {@link Bundle}s are 12 | * used to group multiple {@link Module}s and {@link Bundle}s what allows to 13 | * build graphs of {@link Bundle}s with {@link Module}s as leaves. 14 | * 15 | * @see Bundle 16 | * @see ModuleWith 17 | */ 18 | @FunctionalInterface 19 | public interface Module { 20 | 21 | /** 22 | * @param bindings use to declare made bound within this {@link Module}. 23 | * @param env the {@link Env} used during the bootstrapping of the 24 | * {@link Module} 25 | */ 26 | void declare(Bindings bindings, Env env); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.idea/se.jbee.inject.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /se.jbee.inject.contract/main/java/se/jbee/inject/contract/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the add-on to do asynchronous message dispatch build on vanilla Java 3 | * methods. 4 | * 5 | *

Registering Handlers

6 | * Any vanilla java interface can be registered as an event handler using {@link 7 | * se.jbee.inject.contract.ContractModule#handle(java.lang.Class)}. 8 | * 9 | *

Sending Events

10 | * The event handler interface is injected into the calling managed instance and 11 | * the handler method is called. 12 | * 13 | *

Receiving Events

14 | * Any bound and created instance that implements a registered event handler 15 | * interface will asynchronously receive any message send. Either make sure the 16 | * instance is injected during bootstrapping of the application or bind the 17 | * instance in an {@link se.jbee.inject.ScopeLifeCycle#eager()} scope. 18 | */ 19 | package se.jbee.inject.contract; 20 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/scope/ThreadScope.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.scope; 2 | 3 | import se.jbee.inject.*; 4 | 5 | /** 6 | * Asks the {@link Provider} once per thread per {@link Resource} which is 7 | * understand commonly as a usual 'per-thread' singleton. 8 | */ 9 | public final class ThreadScope implements Scope { 10 | 11 | private final ThreadLocal instances = new ThreadLocal<>(); 12 | 13 | @SuppressWarnings("unchecked") 14 | @Override 15 | public T provide(int serialID, int resources, Dependency dep, 16 | Provider provider) throws UnresolvableDependency { 17 | Object[] objects = instances.get(); 18 | if (objects == null) { 19 | objects = new Object[resources]; 20 | instances.set(objects); 21 | } 22 | Object res = objects[serialID]; 23 | if (res == null) { 24 | res = provider.provide(); 25 | objects[serialID] = res; 26 | } 27 | return (T) res; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.idea/bach.project.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/example0/TestServiceLoaderBootstrap.java: -------------------------------------------------------------------------------- 1 | package test.integration.example0; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Env; 5 | import se.jbee.inject.bootstrap.Bootstrap; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertSame; 8 | 9 | /** 10 | * Small test to check that {@link Bootstrap#currentEnv()} and {@link 11 | * Bootstrap#currentInjector()} do the expected thing. 12 | */ 13 | class TestServiceLoaderBootstrap { 14 | 15 | @Test 16 | void serviceLoaderEnvIsOnlyCreatedOnce() { 17 | assertSame(Bootstrap.currentEnv(), Bootstrap.currentEnv()); 18 | } 19 | 20 | @Test 21 | void serviceLoaderInjectorIsOnlyCreatedOnce() { 22 | assertSame(Bootstrap.currentInjector(), Bootstrap.currentInjector()); 23 | } 24 | 25 | @Test 26 | void serviceLoaderInjectorUsesServiceLoaderEnv() { 27 | assertSame(Bootstrap.currentEnv(), Bootstrap.currentInjector().resolve( 28 | Env.class)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/Connector.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.config; 2 | 3 | import se.jbee.lang.Type; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | @FunctionalInterface 8 | public interface Connector { 9 | 10 | /** 11 | * Callback function called by during instance instantiation if methods of 12 | * the instance should be connected elsewhere, namely by the means of this 13 | * {@link Connector} to the endpoint it is connecting. 14 | * 15 | * @param instance the subject instance that should receive the call 16 | * @param as the type as which the instance is used 17 | * (injected/resolved). This is either the exact {@link 18 | * Class} type of the instance or one of its supertypes. 19 | * @param connected the {@link Method} to call for the circumstances 20 | * represented by this {@link Connector} 21 | */ 22 | void connect(Object instance, Type as, Method connected); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Descriptor.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | /** 4 | * A marker interface for types that can be used as a reference to a particular 5 | * way to create bindings. 6 | *

7 | * It is in some sense a source value that can be turned into one or more 8 | * bindings during the binding process. 9 | *

10 | * The reason this is defined in the core is simply that some of the core types 11 | * should implement it. Otherwise this would be found in the binding module. 12 | * 13 | * @since 8.1 14 | */ 15 | public interface Descriptor { 16 | 17 | /* just a marker */ 18 | 19 | 20 | final class BridgeDescriptor implements Descriptor { 21 | 22 | public final Class type; 23 | 24 | public BridgeDescriptor(Class type) { 25 | this.type = type; 26 | } 27 | } 28 | 29 | final class ArrayDescriptor implements Descriptor { 30 | public final Hint[] elements; 31 | 32 | public ArrayDescriptor(Hint[] elements) { 33 | this.elements = elements; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains all essential concepts and types of the library. The user facing API 3 | * is the {@link se.jbee.inject.Injector}. 4 | * 5 | *

Code organisation

6 | *

7 | * The root package with it essential types does not depend on any of its 8 | * sub-packages, the sub-packages depend on the root. 9 | *

10 | * 11 | *

Initialising Objects Created In Context

12 | *

13 | * To initialise objects created in the {@link se.jbee.inject.Injector} bind a 14 | * {@link se.jbee.inject.Lift} for the {@link se.jbee.lang.Type} of 15 | * objects it should initialise. This is mostly equivalent to a 16 | * javax.annotation.PostConstruct annotation just that the effect 17 | * is not given by an annotated method but by the provided {@link 18 | * se.jbee.inject.Lift} function. This can equally be applied to the 19 | * {@link se.jbee.inject.Injector} itself to wrap it. The bootstrapping will 20 | * return the outermost wrapper. 21 | *

22 | */ 23 | package se.jbee.inject; 24 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/bootstrap/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the core utility to {@link se.jbee.inject.bootstrap.Bootstrap} a 3 | * {@link se.jbee.inject.Injector} or {@link se.jbee.inject.Env} from a root 4 | * {@link se.jbee.inject.bind.Bundle}. 5 | * 6 | *

Custom Environments

7 | *

8 | * The {@link se.jbee.inject.bootstrap.Bootstrap#DEFAULT_ENV} is a default 9 | * implementation of {@link se.jbee.inject.Env} in form of a map. More complex 10 | * setup can be done by bootstrapping the {@link se.jbee.inject.Env} itself from 11 | * a root {@link se.jbee.inject.bind.Bundle} and convert the resulting {@link 12 | * se.jbee.inject.Injector} to an {@link se.jbee.inject.Env} using {@link 13 | * se.jbee.inject.Injector#asEnv()}. Most often such a setup will use {@link 14 | * se.jbee.inject.Env#with(se.jbee.inject.Name, Type, 15 | * java.lang.Object)} to add properties to the resulting environment that are 16 | * only available at runtime like command line arguments. 17 | *

18 | */ 19 | package se.jbee.inject.bootstrap; 20 | 21 | import se.jbee.lang.Type; 22 | -------------------------------------------------------------------------------- /test.examples/test/java/test/example1/SupportAnnotationTemplet.java: -------------------------------------------------------------------------------- 1 | package test.example1; 2 | 3 | import se.jbee.inject.Extends; 4 | import se.jbee.inject.Scope; 5 | import se.jbee.inject.binder.BinderModuleWith; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.ServiceLoader; 9 | 10 | /** 11 | * This is bound to the annotation 12 | * using {@link ServiceLoader}. 13 | * 14 | * There are two ways to link this class to an {@link Annotation}. 15 | * 16 | * 1. Annotated this class with the {@link Annotation} it implements (works only 17 | * if there is no other annotation present) 18 | * 19 | * 2. Use {@link Extends} annotation to point out the {@link Annotation} 20 | * {@link Class} to link this class with 21 | * 22 | * As {@link Extends} allows other annotations to be present it should be 23 | * preferred where possible. 24 | */ 25 | @Extends(Support.class) 26 | public class SupportAnnotationTemplet extends BinderModuleWith> { 27 | 28 | @Override 29 | protected void declare(Class annotated) { 30 | per(Scope.application).withPublishedAccess().bind(annotated).toConstructor(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/FilteredServiceLoaderBundles.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder; 2 | 3 | import se.jbee.inject.bind.Bundle; 4 | 5 | import java.util.ServiceLoader; 6 | 7 | public abstract class FilteredServiceLoaderBundles extends BootstrapperBundle { 8 | 9 | @Override 10 | protected final void bootstrap() { 11 | for (Bundle bundle : ServiceLoader.load(Bundle.class)) { 12 | Class bundleId = bundle.getClass(); 13 | if (bootstrap(bundleId)) { 14 | install(bundleId); 15 | } 16 | } 17 | } 18 | 19 | /** 20 | * In the instance of {@link Bundle}s composition isn't as meaningful as in 21 | * most other contexts as {@link Bundle} are purely identified by {@link 22 | * Class}. This means two instances of the same {@link Bundle} 23 | * implementation with different inner state make no sense. Therefore we do 24 | * use inheritance over composition. 25 | * 26 | * @param bundle a {@link Bundle} provided by {@link ServiceLoader} 27 | * @return true, if the bundle should be installed, else false 28 | */ 29 | abstract boolean bootstrap(Class bundle); 30 | } 31 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/scope/ApplicationScope.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.scope; 2 | 3 | import se.jbee.inject.*; 4 | 5 | import java.util.concurrent.atomic.AtomicReference; 6 | import java.util.concurrent.atomic.AtomicReferenceArray; 7 | 8 | /** 9 | * Asks the {@link Provider} once per binding. Thereby instances become 10 | * singletons local to the application. 11 | * 12 | * Will lead to instances that can be seen as application-wide-singletons. 13 | * 14 | * Contains an instance per {@link Generator}. Instances are never updated. 15 | */ 16 | public final class ApplicationScope implements Scope { 17 | 18 | private final AtomicReference> instances = new AtomicReference<>(); 19 | 20 | @SuppressWarnings("unchecked") 21 | @Override 22 | public T provide(int serialID, int resources, Dependency dep, 23 | Provider provider) throws UnresolvableDependency { 24 | return (T) instances.updateAndGet(objs -> objs != null 25 | ? objs 26 | : new AtomicReferenceArray<>(resources)).updateAndGet(serialID, 27 | obj -> obj != null ? obj : provider.provide()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the {@link se.jbee.inject.action.Action} add-on implementation. 3 | *

4 | * {@link se.jbee.inject.action.Action}s are abstractions for vanilla Java 5 | * methods which are identified by their parameter and return type pair. 6 | *

7 | * Callers can inject them as {@link se.jbee.inject.action.Action} without 8 | * becoming dependent on the provider of the underlying implementation. This 9 | * allows a very loosely coupled highly modularised application composed out of 10 | * modules that do not need to know each other. The contract between the 11 | * application module are the data types of parameter and return types. 12 | *

13 | * {@link se.jbee.inject.action.Action}s can also be used do emulate method 14 | * interception without bytecode manipulation. All calls to {@link 15 | * se.jbee.inject.action.Action}s are executed using the {@link 16 | * se.jbee.inject.action.ActionExecutor}. Any custom interception can be implemented 17 | * by providing a custom {@link se.jbee.inject.action.ActionExecutor} implementation. 18 | */ 19 | package se.jbee.inject.action; 20 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/ConfiguringInstanceLocalBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.Instance; 4 | import se.jbee.inject.config.Config; 5 | 6 | /** 7 | * Makes a configuration binding. Any configuration is local to a target {@link 8 | * Instance}. 9 | * 10 | * @since 8.1 11 | * 12 | * @param return type of the binder step following a {@code configure} step 13 | */ 14 | @FunctionalInterface 15 | public interface ConfiguringInstanceLocalBinder> extends 16 | InstanceLocalBinder { 17 | 18 | /** 19 | * Root for container "global" configuration. 20 | * 21 | * @since 8.1 22 | */ 23 | default B configure() { 24 | return injectingInto(Config.class); 25 | } 26 | 27 | /** 28 | * Root for target type specific configuration. 29 | * 30 | * @since 8.1 31 | */ 32 | default B configure(Class ns) { 33 | return configure().within(ns); 34 | } 35 | 36 | /** 37 | * Root for {@link Instance} specific configuration. 38 | * 39 | * @since 8.1 40 | */ 41 | default B configure(Instance ns) { 42 | return configure().within(ns); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/ReferenceBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.Instance; 4 | import se.jbee.inject.Name; 5 | import se.jbee.lang.Type; 6 | 7 | import static se.jbee.inject.Instance.instance; 8 | import static se.jbee.inject.Name.named; 9 | import static se.jbee.lang.Type.raw; 10 | 11 | /** 12 | * Bind a resource to a forward referenced {@link Instance}. 13 | *

14 | * This is practically publishing the referenced {@link Instance} as the bound 15 | * "API" resource. 16 | * 17 | * @param type of the bound "API" resource (minimum required type) 18 | */ 19 | @FunctionalInterface 20 | public interface ReferenceBinder { 21 | 22 | void to(Instance instance); 23 | 24 | default void to(String name, Class type) { 25 | to(named(name), type); 26 | } 27 | 28 | default void to(Name name, Class type) { 29 | to(instance(name, raw(type))); 30 | } 31 | 32 | default void to(Name name, Type type) { 33 | to(instance(name, type)); 34 | } 35 | 36 | default void to(Class impl) { 37 | to(Instance.anyOf(raw(impl))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /se.jbee.inject.convert/main/java/se/jbee/inject/convert/Imports.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.convert; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.*; 9 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 10 | 11 | /** 12 | * Works similar to an import statement in a source file. 13 | * The referenced {@link Class}es are the imported fully qualified types. 14 | * Their simple name is used in other annotations and this annotation is used 15 | * to determine the fully qualified name. 16 | * 17 | * In particular this is used in combination with {@link Converts}. 18 | * 19 | * @see Converts 20 | */ 21 | @Inherited 22 | @Documented 23 | @Retention(RUNTIME) 24 | @Target({ TYPE, METHOD, FIELD, CONSTRUCTOR, ANNOTATION_TYPE, PARAMETER }) 25 | public @interface Imports { 26 | 27 | /** 28 | * @return A list of {@link Class} to import so their {@link 29 | * Class#getSimpleName()} can be used in properties of other annotations to 30 | * refer to a fully qualified class. 31 | */ 32 | Class[] value(); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestExampleLambdaBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.bind.Bootstrapper; 6 | import se.jbee.inject.binder.AbstractBinderModule; 7 | import se.jbee.inject.binder.BinderModule; 8 | import se.jbee.inject.bootstrap.Bootstrap; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | class TestExampleLambdaBinds { 13 | 14 | static class TestExampleLambdaBindsBundle extends AbstractBinderModule { 15 | 16 | @Override 17 | public void bootstrap(Bootstrapper bootstrap) { 18 | bootstrap.install((bindings, env) 19 | -> bindings.declaredFrom(env, 20 | new TestExampleLambdaBindsModule())); 21 | } 22 | 23 | } 24 | 25 | static class TestExampleLambdaBindsModule extends BinderModule { 26 | 27 | @Override 28 | protected void declare() { 29 | bind(int.class).to(1); 30 | } 31 | 32 | } 33 | 34 | @Test 35 | void thatLambdasCanBeUsedToDescribeModules() { 36 | Injector injector = Bootstrap.injector(TestExampleLambdaBindsModule.class); 37 | 38 | assertEquals(1, injector.resolve(int.class).intValue()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicConstantBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | import se.jbee.lang.Type; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static se.jbee.lang.Type.raw; 11 | 12 | class TestBasicConstantBinds { 13 | 14 | private static class ConstantBindsModule extends BinderModule { 15 | 16 | @Override 17 | protected void declare() { 18 | bind(String.class).to("foobar"); 19 | bind(CharSequence.class).to("bar"); 20 | bind(Integer.class).to(42); 21 | bind(Float.class).to(42.0f); 22 | } 23 | } 24 | 25 | private final Injector context = Bootstrap.injector( 26 | ConstantBindsModule.class); 27 | 28 | @Test 29 | void instanceInjectedBasedOnTheDependencyType() { 30 | assertInjects("bar", raw(CharSequence.class)); 31 | assertInjects("foobar", raw(String.class)); 32 | assertInjects(42, raw(Integer.class)); 33 | } 34 | 35 | private void assertInjects(T expected, Type forActual) { 36 | assertEquals(expected, context.resolve(forActual)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicInstanceBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | /** 11 | * The test demonstrates binds that are 'linked' by type. 12 | */ 13 | class TestBasicInstanceBinds { 14 | 15 | public static class Foo { 16 | 17 | } 18 | 19 | private static class TestBasicInstanceBindsModule extends BinderModule { 20 | 21 | @Override 22 | protected void declare() { 23 | bind(Number.class).to(Integer.class); 24 | bind(Integer.class).to(42); 25 | bind(Foo.class).to(Foo.class); 26 | } 27 | } 28 | 29 | private final Injector injector = Bootstrap.injector( 30 | TestBasicInstanceBindsModule.class); 31 | 32 | @Test 33 | void thatNumberDependencyIsResolvedToIntegerBoundSupplier() { 34 | Number number = injector.resolve(Number.class); 35 | assertTrue(number instanceof Integer); 36 | assertEquals(42, number.intValue()); 37 | } 38 | 39 | @Test 40 | void thatTypeLinkedBackToItselfBecomesConstructorBinding() { 41 | assertNotNull(injector.resolve(Foo.class)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /se.jbee.junit.assertion/test/java/se/jbee/junit/assertion/Await.java: -------------------------------------------------------------------------------- 1 | package se.jbee.junit.assertion; 2 | 3 | import java.time.Duration; 4 | 5 | import static java.lang.Math.max; 6 | import static java.lang.Math.min; 7 | import static java.time.Duration.ofMillis; 8 | 9 | public final class Await { 10 | 11 | public static Await atMost(Duration maxWaitTime) { 12 | return new Await(Duration.ZERO, maxWaitTime, Duration.ZERO); 13 | } 14 | 15 | private final Duration pollInterval; 16 | public final Duration maxWaitTime; 17 | public final Duration minWaitTime; 18 | 19 | private Await(Duration pollInterval, Duration maxWaitTime, 20 | Duration minWaitTime) { 21 | this.pollInterval = pollInterval; 22 | this.maxWaitTime = maxWaitTime; 23 | this.minWaitTime = minWaitTime; 24 | } 25 | 26 | public Await tryInInterval(Duration pollInterval) { 27 | return new Await(pollInterval, maxWaitTime, minWaitTime); 28 | } 29 | 30 | public Await atLeast(Duration minWaitTime) { 31 | return new Await(pollInterval, maxWaitTime, minWaitTime); 32 | } 33 | 34 | public Duration pollInterval() { 35 | if (!pollInterval.isZero()) 36 | return pollInterval; 37 | long maxWaitMillis = maxWaitTime.toMillis(); 38 | return ofMillis(max(1, min(100, maxWaitMillis / 10))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/event/RecordingScheduledExecutor.java: -------------------------------------------------------------------------------- 1 | package test.integration.event; 2 | 3 | import se.jbee.inject.schedule.SchedulerModule; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.concurrent.CompletableFuture; 8 | import java.util.concurrent.Future; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | class RecordingScheduledExecutor 12 | implements SchedulerModule.ScheduledExecutor { 13 | 14 | static final class Job { 15 | final Runnable task; 16 | final long initialDelay; 17 | final long period; 18 | final TimeUnit unit; 19 | 20 | Job(Runnable task, long initialDelay, long period, 21 | TimeUnit unit) { 22 | this.task = task; 23 | this.initialDelay = initialDelay; 24 | this.period = period; 25 | this.unit = unit; 26 | } 27 | } 28 | 29 | final List recorded = new ArrayList<>(); 30 | 31 | @Override 32 | public Future executeInSchedule(Runnable task, long initialDelay, 33 | long period, TimeUnit unit) { 34 | Job job = new Job(task, initialDelay, period, unit); 35 | recorded.add(job); 36 | return CompletableFuture.completedFuture(job); 37 | } 38 | 39 | Job lastRecorded() { 40 | return recorded.isEmpty() ? null : recorded.get(recorded.size() - 1); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /se.jbee.lang/main/java/se/jbee/lang/Lazy.java: -------------------------------------------------------------------------------- 1 | package se.jbee.lang; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | import java.util.concurrent.atomic.AtomicReference; 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * The {@link Lazy} utility class is used for fields that are initialised by a 9 | * {@link Supplier} function that should only run once in case it will be 10 | * initialising the value. 11 | * 12 | * It should not be run at all if the value is uninitialised or another thread 13 | * will succeed in initialising it. 14 | * 15 | * It should also not run multiple times to successful initialise the field. 16 | * 17 | * @author Jan Bernitt 18 | * 19 | * @param Type of the value 20 | */ 21 | public final class Lazy extends AtomicReference { 22 | 23 | private final AtomicBoolean initialised = new AtomicBoolean(); 24 | 25 | public V get(Supplier initialValue) { 26 | V value; 27 | if (initialised.compareAndSet(false, true)) { 28 | value = initialValue.get(); 29 | set(value); 30 | } else { 31 | // another thread is about to initialise it 32 | do { 33 | value = get(); 34 | } while (value == null); 35 | } 36 | return value; 37 | } 38 | 39 | public boolean isInitialised() { 40 | return initialised.get(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Verifier.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | /** 4 | * {@link Verifier}s are attached to 5 | */ 6 | @FunctionalInterface 7 | public interface Verifier { 8 | 9 | Verifier AOK = context -> {}; 10 | 11 | /** 12 | * Called by the {@link Injector} at the end of bootstrapping process for 13 | * each {@link Resource} that became part of the container context. This 14 | * occurs after the {@link Injector} itself has been initialised by {@link 15 | * Lift}s but before eager scoped instances are created. 16 | * 17 | * @param context the container to use to verify the conditions captured by 18 | * this {@link Verifier}. 19 | * @throws InconsistentDeclaration In case the verification concludes that 20 | * the context isn't valid. For example a 21 | * verifier could check that each of the 22 | * parameters of a bound constructor can be 23 | * resolved to a {@link Resource} 24 | */ 25 | void verifyIn(Injector context) throws InconsistentDeclaration; 26 | 27 | 28 | default Verifier and(Verifier other) { 29 | return context -> { 30 | verifyIn(context); 31 | other.verifyIn(context); 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestFeatureTypeDependentScopes.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Provider; 5 | import se.jbee.inject.Scope; 6 | import se.jbee.inject.scope.TypeDependentScope; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertSame; 9 | import static se.jbee.inject.Dependency.dependency; 10 | 11 | /** 12 | * A basic test verifying the {@link TypeDependentScope} implementation behaves 13 | * as expected. 14 | */ 15 | class TestFeatureTypeDependentScopes { 16 | 17 | static class A { 18 | // just for test 19 | } 20 | 21 | static class B { 22 | // just for test 23 | } 24 | 25 | @Test 26 | void dependencyTypeScopeEnsuresSingletonPerExactGenericType() { 27 | Scope scope = TypeDependentScope.perTypeSignature(); 28 | A a = new A(); 29 | B b = new B(); 30 | Provider ia = () -> a; 31 | Provider ib = () -> b; 32 | assertSame(scope.provide(1, 2, dependency(A.class), ia), a); 33 | assertSame(scope.provide(1, 2, dependency(A.class), null), a); // the null Provider shouldn't be called now 34 | assertSame(scope.provide(2, 2, dependency(B.class), ib), b); 35 | assertSame(scope.provide(2, 2, dependency(B.class), null), b); // the null Provider shouldn't be called now 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/ActionExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.action; 7 | 8 | import se.jbee.inject.UnresolvableDependency.SupplyFailed; 9 | 10 | import java.lang.reflect.Method; 11 | 12 | /** 13 | * The {@link ActionExecutor} invokes the actual action {@link Method}. It is an 14 | * abstraction for the inner mechanics of {@link Action}s so that these can be 15 | * customised by replacing the {@link ActionExecutor} by making a bind (see 16 | * {@link ActionModule}). 17 | * 18 | * @see Action 19 | */ 20 | @FunctionalInterface 21 | public interface ActionExecutor { 22 | 23 | /** 24 | * Runs an {@link Action} by invoking the underlying method. 25 | * 26 | * @param args all resolved arguments for the method (in order) 27 | * @param value provided (also one of the arguments) 28 | * @throws ActionExecutionFailed in case of any {@link Exception} during 29 | * execution. The cause should be the exception causing the 30 | * problem, not another wrapper like {@link SupplyFailed}. 31 | */ 32 | B execute(ActionSite site, Object[] args, A value) 33 | throws ActionExecutionFailed; 34 | } 35 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/schedule/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains components of a basic add-on to do scheduled {@link 3 | * java.lang.reflect.Method} calls. 4 | *

5 | * The methods to call are identified using {@connect} feature which is based on 6 | * {@link se.jbee.inject.Lift}s. 7 | *

8 | * The {@link se.jbee.inject.schedule.Scheduled} annotation can be used to mark 9 | * methods that should be called and define their {@link 10 | * se.jbee.inject.schedule.Schedule}. 11 | *

12 | * Annotated methods can have parameters which are injected. The idea is to 13 | * reverse the code dependencies. Instead of making all classes dependent on the 14 | * code that runs the scheduling the desired schedule is declared in a 15 | * declarative way. This allows to switch the executing code without modifying 16 | * any of the classes that have the need for scheduled tasks. Also all 17 | * dependencies that are only needed to run the task body can be made parameters 18 | * of the scheduled method reducing the need for fields that are otherwise not 19 | * used in the class. 20 | *

21 | * Users can add custom ways to derive a {@link se.jbee.inject.schedule.Schedule} 22 | * by registering a named {@link se.jbee.inject.schedule.Schedule.ScheduleFactory}. 23 | */ 24 | package se.jbee.inject.schedule; 25 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/PackageLocalBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.Packages; 4 | 5 | import static se.jbee.inject.Packages.*; 6 | 7 | /** 8 | * Make a binding local to a specific package. This means the binding only 9 | * applies for an injection if the injected bean class is defined within the 10 | * packages declared. 11 | * 12 | * @since 8.1 13 | * 14 | * @param return type of the binder step following a {@code in} step 15 | */ 16 | @FunctionalInterface 17 | public interface PackageLocalBinder { 18 | 19 | /** 20 | * Make all bindings made with the returned binder local to the provided set 21 | * of {@link Packages}. 22 | * 23 | * @param packages a set of {@link Packages} all following binding should 24 | * apply to 25 | * @return next step in fluent API 26 | */ 27 | B in(Packages packages); 28 | 29 | /** 30 | * @see #in(Packages) 31 | */ 32 | default B inPackageAndSubPackagesOf(Class type) { 33 | return in(packageAndSubPackagesOf(type)); 34 | } 35 | 36 | /** 37 | * @see #in(Packages) 38 | */ 39 | default B inPackageOf(Class type) { 40 | return in(packageOf(type)); 41 | } 42 | 43 | /** 44 | * @see #in(Packages) 45 | */ 46 | default B inSubPackagesOf(Class type) { 47 | return in(subPackagesOf(type)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestExampleDecoratorBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | /** 11 | * Test the fix for issue #61, falsely detection of dependency cycle when using 12 | * the decorator pattern. 13 | */ 14 | class TestExampleDecoratorBinds { 15 | 16 | interface Foo { 17 | // the used abstraction 18 | } 19 | 20 | public static class Decorator implements Foo { 21 | 22 | final Foo decorated; 23 | 24 | public Decorator(Foo decorated) { 25 | this.decorated = decorated; 26 | } 27 | 28 | } 29 | 30 | public static class Bar implements Foo { 31 | // a special Foo for the Decorator 32 | } 33 | 34 | static class TestExampleDecoratorBindsModule extends BinderModule { 35 | 36 | @Override 37 | protected void declare() { 38 | bind(Foo.class).to(Decorator.class); 39 | injectingInto(Decorator.class).bind(Foo.class).to(Bar.class); 40 | } 41 | } 42 | 43 | @Test 44 | void decoratorPatternCanBeUsed() { 45 | Injector injector = Bootstrap.injector(TestExampleDecoratorBindsModule.class); 46 | assertEquals(Decorator.class, injector.resolve(Foo.class).getClass()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/api/TestResource.java: -------------------------------------------------------------------------------- 1 | package test.integration.api; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.*; 5 | import se.jbee.lang.Type; 6 | 7 | import java.io.Serializable; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | import static java.util.stream.Collectors.toList; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static se.jbee.lang.Type.raw; 16 | 17 | class TestResource { 18 | 19 | @Test 20 | void naturalOrderIsFromMostQualifiedToLeastQualified() { 21 | List> resources = new ArrayList<>(); 22 | resources.add(createResourceOf(1, Type.WILDCARD)); 23 | resources.add( 24 | createResourceOf(2, raw(Serializable.class).asUpperBound())); 25 | resources.add(createResourceOf(3, raw(Number.class).asUpperBound())); 26 | Collections.sort(resources); 27 | assertEquals(Arrays.asList(3, 2, 1), 28 | resources.stream().map(e -> e.serialID).collect(toList())); 29 | } 30 | 31 | private Resource createResourceOf(int serialID, Type type) { 32 | Source source = Source.source(getClass()); 33 | return new Resource<>(serialID, source, ScopeLifeCycle.ignore, 34 | new Locator<>(Instance.anyOf(type)), 35 | Annotated.EMPTY, Verifier.AOK, 36 | resource -> (dep -> null)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/On.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.METHOD; 8 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 | 10 | @Documented 11 | @Retention(RUNTIME) 12 | @Target(METHOD) 13 | public @interface On { 14 | 15 | interface Aware {} 16 | 17 | enum DispatchType { 18 | /** 19 | * Returned events are first processed when all receivers of the 20 | * currently processed event have received it. 21 | */ 22 | SEQUENTIAL, 23 | /** 24 | * Returned events are processed directly after they have been returned. 25 | * This might be before further receivers receive the currently 26 | * processed event or after. 27 | */ 28 | INTERLEAVED 29 | } 30 | 31 | /** 32 | * An event triggered when the JVM is shutting down. 33 | */ 34 | class Shutdown {} 35 | 36 | Class[] value(); 37 | 38 | DispatchType proceed() default DispatchType.SEQUENTIAL; 39 | 40 | //TODO what to do when receiving threw an exception => strategy interface referenced by class? or better build on bindings for more flexibility and consistency of event types? 41 | // maybe even both: method level is for individual setting, default is "use global", global is via binding 42 | } 43 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/SimpleModule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder; 2 | 3 | import se.jbee.inject.Env; 4 | import se.jbee.inject.Scope; 5 | import se.jbee.inject.bind.Module; 6 | import se.jbee.inject.bind.*; 7 | 8 | import static se.jbee.inject.Source.source; 9 | 10 | /** 11 | * An alternative simplified binder API that is particularly useful as it does 12 | * not require any {@link Env} properties to work. 13 | *

14 | * Therefore it is for example used to bootstrap the initial {@link Env}. 15 | *

16 | * By default this API binds all bindings as {@link se.jbee.inject.DeclarationType#DEFAULT}. 17 | * 18 | * @since 8.1 19 | */ 20 | public abstract class SimpleModule extends SimpleBinder 21 | implements Bundle, Module { 22 | 23 | private Boolean declaring; 24 | 25 | protected SimpleModule() { 26 | super(Bind.UNINITIALIZED.per(Scope.container)); 27 | } 28 | 29 | protected abstract void declare(); 30 | 31 | @Override 32 | public final void bootstrap(Bootstrapper bootstrap) { 33 | bootstrap.install(this); 34 | } 35 | 36 | @Override 37 | public final void declare(Bindings bindings, Env env) { 38 | InconsistentBinding.nonnullThrowsReentranceException(declaring); 39 | configure(bind -> bind 40 | .into(env, bindings) 41 | .with(source(getClass())) 42 | .asDefault()); 43 | declaring = true; 44 | declare(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventTrigger.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | import se.jbee.lang.Type; 4 | 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * An {@link EventTrigger} is an independent source of a particular type of 9 | * event. 10 | *

11 | * When an event type has known receivers the corresponding {@link EventTrigger} 12 | * is resolved and {@link #activate(Consumer)} is called passing in a callback 13 | * to the dispatch which should be used by the trigger in case it wants to 14 | * trigger an event of its type in the future. 15 | * 16 | * @param type of the event triggered by the implementation 17 | */ 18 | @FunctionalInterface 19 | public interface EventTrigger { 20 | 21 | static Type> eventTriggerTypeOf(Class event) { 22 | return eventTriggerTypeOf(Type.raw(event)); 23 | } 24 | 25 | @SuppressWarnings({ "unchecked", "rawtypes" }) 26 | static Type> eventTriggerTypeOf(Type event) { 27 | return (Type) Type.raw(EventTrigger.class).parameterized(event); 28 | } 29 | 30 | /** 31 | * A receiver for the event type has been encountered, the trigger is asked 32 | * from now on to trigger event by passing the event object to the provided 33 | * dispatcher callback. 34 | * 35 | * @param dispatcher should be called to trigger events 36 | */ 37 | void activate(Consumer dispatcher); 38 | } 39 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicInjectorExceptionsBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.Name; 6 | import se.jbee.inject.UnresolvableDependency; 7 | import se.jbee.inject.UnresolvableDependency.ResourceResolutionFailed; 8 | import se.jbee.inject.binder.BinderModule; 9 | import se.jbee.inject.bootstrap.Bootstrap; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertThrows; 12 | import static se.jbee.inject.Name.named; 13 | 14 | class TestBasicInjectorExceptionsBinds { 15 | 16 | private static class TestBasicInjectorExceptionsBindsModule 17 | extends BinderModule { 18 | 19 | @Override 20 | protected void declare() { 21 | bind(named("foo"), Integer.class).to(7); 22 | bind(named("bar"), Integer.class).to(8); 23 | } 24 | } 25 | 26 | private final Injector context = Bootstrap.injector( 27 | TestBasicInjectorExceptionsBindsModule.class); 28 | 29 | @Test 30 | void exceptionIsThrownWhenResolvingAnUnboundDependency() { 31 | assertThrows(UnresolvableDependency.ResourceResolutionFailed.class, 32 | () -> context.resolve(String.class)); 33 | } 34 | 35 | @Test 36 | void exceptionIsThrownWhenResolvingAnUnboundDependencyWithBoundRawType() { 37 | assertThrows(ResourceResolutionFailed.class, 38 | () -> context.resolve(Name.DEFAULT, Integer.class)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/MulticastDispatch.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.action; 2 | 3 | import se.jbee.inject.DisconnectException; 4 | import se.jbee.inject.Injector; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * The {@link MulticastDispatch} calls all {@link ActionSite}s and returns the 10 | * last successful result. 11 | */ 12 | public final class MulticastDispatch implements 13 | ActionDispatch { 14 | 15 | private final Injector context; 16 | private final ActionExecutor executor; 17 | 18 | public MulticastDispatch(Injector context, ActionExecutor executor) { 19 | this.context = context; 20 | this.executor = executor; 21 | } 22 | 23 | @Override 24 | public B execute(A input, List> sites) { 25 | ActionExecutionFailed ex = null; 26 | int disconnected = 0; 27 | B res = null; 28 | for (ActionSite site : sites) { 29 | try { 30 | res = executor.execute(site, site.args(context, input), input); 31 | } catch (DisconnectException e) { 32 | // not incrementing the index as element at that index now is the next in line 33 | disconnected++; 34 | } catch (ActionExecutionFailed e) { 35 | ex = e; 36 | } 37 | } 38 | if (sites.size() <= disconnected) 39 | throw new DisconnectException("All sites disconnected"); 40 | if (res != null) 41 | return res; 42 | if (ex != null) 43 | throw ex; 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /se.jbee.inject.container/main/java/se/jbee/inject/container/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * This package contains the default implementation for the {@link 4 | * se.jbee.inject.Injector} context. It is created from a list of {@link 5 | * se.jbee.inject.ResourceDescriptor}s. Once a context is created the list of 6 | * {@link se.jbee.inject.Resource}s it contains it immutable. This means the 7 | * type of things it knows how to resolve is fixed. 8 | *

9 | * 10 | *

{@link se.jbee.inject.Injector} Features

11 | *

12 | * Many of the "basic" features of the {@link se.jbee.inject.Injector} are actually 13 | * defined in user space in form of {@code se.jbee.inject.bind.Module}s that make 14 | * bindings to produce {@link se.jbee.inject.ResourceDescriptor}s. 15 | * 16 | * For example {@link se.jbee.inject.Scope}s are usual {@link 17 | * se.jbee.inject.Resource}s that are bound to their implementations by default 18 | * by the {@code se.jbee.inject.defaults.DefaultScopes} module. 19 | *

20 | * 21 | *

A Note on Independence

22 | *

23 | * The {@link se.jbee.inject.container.Container} implementation is independent 24 | * of the {@code se.jbee.inject.bind.Binding} based fluent API. Its bindings 25 | * extends the {@link se.jbee.inject.ResourceDescriptor} type that is the basis 26 | * of creating the actual container implementation. 27 | *

28 | */ 29 | package se.jbee.inject.container; 30 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/Constant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.binder; 7 | 8 | import se.jbee.inject.Descriptor; 9 | import se.jbee.inject.Scope; 10 | import se.jbee.inject.bind.ValueBinder; 11 | 12 | /** 13 | * A {@link Constant} is the {@link ValueBinder} expansion wrapper type for any 14 | * constant bound to in the fluent binder API. 15 | * 16 | * @param Type of the constant value 17 | */ 18 | public final class Constant implements Descriptor { 19 | 20 | public final T value; 21 | public final boolean autoBindExactType; 22 | /** 23 | * True in case the {@link Scope} and its effects should be applied, else 24 | * false. By default constants are assumed to be value types that are not 25 | * scoped. 26 | */ 27 | public final boolean scoped; 28 | 29 | public Constant(T value) { 30 | this(value, true, false); 31 | } 32 | 33 | private Constant(T value, boolean autoBindExactType, boolean scoped) { 34 | this.value = value; 35 | this.autoBindExactType = autoBindExactType; 36 | this.scoped = scoped; 37 | } 38 | 39 | public Constant scoped() { 40 | return new Constant<>(value, autoBindExactType, true); 41 | } 42 | 43 | public Constant manual() { 44 | return new Constant<>(value, false, scoped); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/scope/SnapshotScope.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.scope; 2 | 3 | import se.jbee.inject.Dependency; 4 | import se.jbee.inject.Provider; 5 | import se.jbee.inject.Scope; 6 | import se.jbee.inject.UnresolvableDependency; 7 | 8 | /** 9 | * The 'synchronous'-{@link Scope} will be asked first passing a special 10 | * resolver that will ask the 'asynchronous' repository when invoked. Thereby 11 | * the repository originally bound will be asked once. Thereafter the result is 12 | * stored in the synchronous repository. 13 | * 14 | * Both repositories will remember the resolved instance whereby the repository 15 | * considered as the synchronous-repository will deliver a consistent image of 16 | * the world as long as it exists. 17 | * 18 | * @author Jan Bernitt (jan@jbee.se) 19 | */ 20 | public final class SnapshotScope implements Scope { 21 | 22 | public static Scope asSnapshot(Scope src, Scope dest) { 23 | return new SnapshotScope(src, dest); 24 | } 25 | 26 | private final Scope src; 27 | private final Scope dest; 28 | 29 | private SnapshotScope(Scope src, Scope dest) { 30 | this.src = src; 31 | this.dest = dest; 32 | } 33 | 34 | @Override 35 | public T provide(int serialID, int resources, Dependency dep, 36 | Provider provider) throws UnresolvableDependency { 37 | return dest.provide(serialID, resources, dep, 38 | () -> src.provide(serialID, resources, dep, provider)); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/RoundRobinDispatch.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.action; 2 | 3 | import se.jbee.inject.DisconnectException; 4 | import se.jbee.inject.Injector; 5 | 6 | import java.util.List; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | /** 10 | * The {@link RoundRobinDispatch} tries to use all available {@link ActionSite} 11 | * equally often. 12 | *

9 | * One way to access a field only visible within their declaring classes package 10 | * is to bind a local version of {@link Get}: 11 | *

12 |  * locally().bind(Get.class).to(Field::get);
13 |  * 
14 | *

15 | * Alternatively the implementation of {@link Get} could use {@link 16 | * java.lang.reflect.AccessibleObject#setAccessible(boolean)} which with java 17 | * module system will require to open the module accordingly. 18 | */ 19 | @FunctionalInterface 20 | public interface Get { 21 | 22 | /** 23 | * An implementation of this method should basically just call {@link 24 | * Field#get(Object)}. 25 | *

26 | * To work around visibility limitations the implementation of this method 27 | * can be defined and bound in the package of the called {@link Field}. 28 | *

29 | * Alternatively an implementation could for example use {@link 30 | * java.lang.reflect.AccessibleObject#setAccessible(boolean)} to make the 31 | * {@link Field} accessible before accessing it. 32 | *

33 | * The implementation could also do an entirely different thing as long as 34 | * the result is equivalent to accessing the provided {@link Field} of the 35 | * provided instance. 36 | * 37 | * @see Field#get(Object) 38 | */ 39 | Object call(Field target, Object instance) throws Exception; 40 | } 41 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Injection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * Describes a "stack-frame" or "layer" within the injection process. 12 | *

13 | * For example, when resolving instance A which causes resolving instance B the 14 | * resolution or injection of A becomes the first frame, the injection of B the 15 | * second and so forth. 16 | */ 17 | public final class Injection implements Serializable { 18 | 19 | public final Instance dependency; 20 | public final Locator target; 21 | public final ScopeLifeCycle lifeCycle; 22 | 23 | public Injection(Instance dependency, Locator target, 24 | ScopeLifeCycle lifeCycle) { 25 | this.dependency = dependency; 26 | this.target = target; 27 | this.lifeCycle = lifeCycle; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object obj) { 32 | return obj instanceof Injection && equalTo((Injection) obj); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return dependency.hashCode() ^ target.hashCode(); 38 | } 39 | 40 | public boolean equalTo(Injection other) { 41 | return this == other || dependency.equalTo(other.dependency) 42 | && target.equalTo(other.target); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return dependency + " from " + target; 48 | } 49 | 50 | public Injection ignoredScoping() { 51 | return new Injection(dependency, target, ScopeLifeCycle.ignore); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/bind/BindingType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bind; 7 | 8 | import se.jbee.inject.Supplier; 9 | 10 | /** 11 | * What is supplying instances for a {@link Binding}? 12 | * 13 | * As we cannot look into the implementation of a {@link Supplier} this type 14 | * groups ways to supply instances. 15 | */ 16 | public enum BindingType { 17 | 18 | /** 19 | * The binding expresses a need, it is unclear if another binding can fulfil 20 | * it. 21 | */ 22 | REQUIRED, 23 | 24 | /** 25 | * The binding is a forward reference to a sub-type (implementation type) or 26 | * to a virtual or generic instance factory like one for lists or other type 27 | * parameterised "bridges". 28 | */ 29 | REFERENCE, 30 | 31 | /** 32 | * The instances are supplied from a {@link Supplier} that has been defined 33 | * before expansion. This might be user defined or hard-wired one within the 34 | * binder API. 35 | */ 36 | PREDEFINED, 37 | 38 | /** 39 | * The instances are supplied by constructing new ones using a constructor. 40 | */ 41 | CONSTRUCTOR, 42 | 43 | /** 44 | * The instances are supplied using a factory method. 45 | */ 46 | METHOD, 47 | 48 | /** 49 | * The instance is supplied from a field. Field is read each time supply 50 | * occurs. 51 | */ 52 | FIELD, 53 | 54 | /** 55 | * The binding is an incomplete value that should be expanded into a 56 | * complete {@link Binding}. 57 | */ 58 | VALUE, 59 | } 60 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Converter.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | import se.jbee.lang.Type; 4 | 5 | /** 6 | * General abstraction for value type conversion. 7 | * 8 | * @author Jan Bernitt 9 | * 10 | * @param type of the input value 11 | * @param type of the output value (converted value) 12 | */ 13 | @FunctionalInterface 14 | public interface Converter { 15 | 16 | static Type> converterTypeOf(Class a, Class b) { 17 | return converterTypeOf(Type.raw(a), Type.raw(b)); 18 | } 19 | 20 | @SuppressWarnings({ "unchecked", "rawtypes" }) 21 | static Type> converterTypeOf(Type a, Type b) { 22 | return (Type) Type.raw(Converter.class).parameterized(a, b); 23 | } 24 | 25 | /** 26 | * Converts input to the corresponding value of the output type of this 27 | * {@link Converter}. 28 | * 29 | * @param input any value including {@code null} 30 | * @return output value including {@code null} 31 | * @throws IllegalArgumentException In case the input value cannot be 32 | * converted to the output type 33 | */ 34 | B convert(A input); 35 | 36 | default Converter then(Converter next) { 37 | return next.upon(this); 38 | } 39 | 40 | default Converter upon(Converter prev) { 41 | return in -> convert(prev.convert(in)); 42 | } 43 | 44 | default Converter orElse(B constant) { 45 | return in -> { 46 | try { 47 | B res = convert(in); 48 | return res == null ? constant : res; 49 | } catch (IllegalArgumentException e) { 50 | return constant; 51 | } 52 | }; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * This package contains {@link se.jbee.inject.bind.Bundle} implementations 4 | * which allow to pick up application level {@link se.jbee.inject.bind.Bundle}s 5 | * using java's {@link java.util.ServiceLoader} mechanism. 6 | *

7 | * 8 | *

Extending the Injector Context

9 | *

10 | * To pick up {@link se.jbee.inject.bind.Bundle}s defined via {@link 11 | * java.util.ServiceLoader} as part of the {@link se.jbee.inject.Injector} 12 | * context install the {@link se.jbee.inject.binder.ServiceLoaderBundles} bundle 13 | * in one of your application's {@link se.jbee.inject.bind.Bundle}s when 14 | * creating the {@link se.jbee.inject.Injector} context. 15 | *

16 | * 17 | *

Extending the Env Context

18 | *

19 | * To pick up {@link se.jbee.inject.bind.Bundle}s defined via {@link 20 | * java.util.ServiceLoader} as part of the {@link se.jbee.inject.Env} context 21 | * install the {@link se.jbee.inject.binder.ServiceLoaderEnvBundles} bundle in 22 | * one of your application's environment {@link se.jbee.inject.bind.Bundle}s 23 | * when creating the {@link se.jbee.inject.Env} context. 24 | *

25 | * 26 | *

Adding Custom Type Level Annotation Definitions

27 | *

28 | * To pick up {@link se.jbee.inject.bind.ModuleWith} defining the effects of an 29 | * particular {@link java.lang.annotation.Annotation} install the {@link 30 | * se.jbee.inject.binder.ServiceLoaderAnnotations} {@link 31 | * se.jbee.inject.bind.Module} as part of the {@link se.jbee.inject.Env} 32 | * context. 33 | *

34 | */ 35 | package se.jbee.inject.binder; 36 | -------------------------------------------------------------------------------- /se.jbee.inject.container/main/java/se/jbee/inject/container/SupplyContext.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.container; 2 | 3 | import se.jbee.inject.*; 4 | 5 | /** 6 | * The {@link SupplyContext} is an abstraction to the internals of a 7 | * {@link Injector} implementation that {@link Generator} use when they actually 8 | * create an instance using the {@link Supplier} which should be called with the 9 | * actual {@link Injector} instance which might not be known when the 10 | * {@link Generator} was created. In a way this is necessary to solve a hen-egg 11 | * situation in the collaboration between {@link Generator} and {@link Injector} 12 | * context implementations. 13 | * 14 | * It is also a bridge between the {@link Generator} on the user side and the 15 | * {@link Supplier} used when binding. 16 | * 17 | * @since 8.1 18 | */ 19 | @FunctionalInterface 20 | public interface SupplyContext { 21 | 22 | /** 23 | * 24 | * @param type of the instance create. 25 | * @param injected the {@link Dependency} currently resolved/injected 26 | * @param supplier the source used to supply the returned instance 27 | * @param resource the {@link Resource} used. Note that the instance cannot 28 | * be supplied using {@link Resource#generate(Dependency)} or any 29 | * other method using the {@link Resource#generator} as that 30 | * generator internally is calling this method. Doing so would 31 | * result in an endless loop. 32 | * @return the instance supplied by the provided {@link Supplier} 33 | */ 34 | T supplyInContext(Dependency injected, 35 | Supplier supplier, Resource resource); 36 | } 37 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/Invoke.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.config; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * {@link Invoke} is the abstraction of {@link Method#invoke(Object, Object...)} 7 | * call so that the actual use of reflection can be customised. 8 | *

9 | * One way to call methods only visible within their declaring classes package 10 | * is to bind a local version of {@link Invoke}: 11 | *

12 |  * locally().bind(Invoke.class).to(Method::invoke);
13 |  * 
14 | *

15 | * Alternatively the implementation of {@link Invoke} could use {@link 16 | * java.lang.reflect.AccessibleObject#setAccessible(boolean)} which with java 17 | * module system will require to open the module accordingly. 18 | */ 19 | @FunctionalInterface 20 | public interface Invoke { 21 | 22 | /** 23 | * An implementation of this method should basically just call {@link 24 | * Method#invoke(Object, Object...)}. 25 | *

26 | * To work around visibility limitations the implementation of this method 27 | * can be defined and bound in the package of the called {@link Method}. 28 | *

29 | * Alternatively an implementation could for example use {@link 30 | * java.lang.reflect.AccessibleObject#setAccessible(boolean)} to make the 31 | * {@link Method} accessible before calling it. 32 | *

9 | * One way to make local classes constructable using reflection is to bind a 10 | * local version of {@link New}. 11 | *

12 |  * locally().bind(New.class).to(Constructor::newInstance);
13 |  * 
14 | *

15 | * Alternatively the implementation of {@link New} could use {@link 16 | * java.lang.reflect.AccessibleObject#setAccessible(boolean)} which with java 17 | * module system will require to open the module accordingly. 18 | */ 19 | @FunctionalInterface 20 | public interface New { 21 | 22 | /** 23 | * An implementation of this method should basically just call {@link 24 | * Constructor#newInstance(Object...)}. 25 | *

26 | * To work around visibility limitations the implementation of this method 27 | * can be defined and bound in the package of the called {@link 28 | * Constructor}. 29 | *

30 | * Alternatively an implementation could for example use {@link 31 | * java.lang.reflect.AccessibleObject#setAccessible(boolean)} to make the 32 | * {@link Constructor} accessible before calling it. 33 | *

34 | * The implementation could also do an entirely different thing as long as 35 | * the result is equivalent to calling the provided {@link Constructor} with 36 | * the provided arguments. 37 | * 38 | * @see Constructor#newInstance(Object...) 39 | */ 40 | T call(Constructor target, Object[] args) throws Exception; 41 | } 42 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/ScopesBy.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.config; 2 | 3 | import se.jbee.inject.Name; 4 | import se.jbee.inject.Scope; 5 | import se.jbee.lang.TypeVariable; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.GenericDeclaration; 9 | import java.lang.reflect.Method; 10 | import java.util.function.Function; 11 | 12 | import static se.jbee.inject.Name.named; 13 | 14 | /** 15 | * Extracts the {@link Name} of the {@link Scope} to use for instances of a 16 | * given type. This can be used to implement scope annotations. 17 | * 18 | * @since 8.1 19 | */ 20 | @FunctionalInterface 21 | public interface ScopesBy { 22 | 23 | ScopesBy AUTO = target -> Scope.auto; 24 | 25 | ScopesBy RETURN_TYPE = target -> { 26 | if (target instanceof Method) { 27 | Method m = (Method) target; 28 | return TypeVariable.typeVariables( 29 | m.getGenericReturnType()).isEmpty() 30 | ? Scope.application 31 | : Scope.dependencyType; 32 | } 33 | return Scope.application; 34 | }; 35 | 36 | Name reflect(GenericDeclaration type); 37 | 38 | default ScopesBy orElse(Name name) { 39 | return obj -> { 40 | Name n = reflect(obj); 41 | return n != null ? n : name; 42 | }; 43 | } 44 | 45 | default ScopesBy orElse(ScopesBy whenNull) { 46 | return obj -> { 47 | Name n = reflect(obj); 48 | return n != null ? n : whenNull.reflect(obj); 49 | }; 50 | } 51 | 52 | static ScopesBy annotatedWith(Class annotation, Function property) { 53 | return obj -> obj.isAnnotationPresent(annotation) // 54 | ? named(property.apply(obj.getAnnotation(annotation))) 55 | : null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * This package contains tests that use the fluent {@link 4 | * se.jbee.inject.binder.Binder} API to setup a test scenario. 5 | *

6 | * This means almost all of them bootstrap an {@link se.jbee.inject.Injector} 7 | * context for that test scenario and verify the behaviour of the resulting 8 | * {@link se.jbee.inject.Injector}. Supposedly this is best described as 9 | * component tests. They test all involved parts from the user facing fluent API 10 | * to declare the test scenario to the inner workings of the {@link 11 | * se.jbee.inject.Injector} to verify the correct behaviour. 12 | *

13 | *

14 | * The tests are grouped in 4 groups: 15 | *

16 | * 17 | *

Basics

18 | *

19 | * Use the name pattern {@code TestBasic*}. They show-case the basics of the 20 | * fluent API and their effects in small self-contained scenarios focussing on a 21 | * single concept or type of binding. They are also meant as a beginners guide 22 | * to learn the fundamentals of the library. 23 | *

24 | * 25 | *

Examples

26 | *

27 | * Use the name pattern {@code TestExample*}. They show-case how to solve a 28 | * particular problem in mostly small self-contained examples. They are also 29 | * meant as a reference or template that can be analysed and adopted to the 30 | * users problem. 31 | *

32 | * 33 | *

Features

34 | *

35 | * Use the name pattern {@code TestFeature*}. They are meant to verify the 36 | * correct behaviour of a single feature. As such they can be used as a 37 | * reference for that particular feature. 38 | *

39 | */ 40 | package test.integration.bind; 41 | -------------------------------------------------------------------------------- /.idea/test.integration.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/BundleFor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.binder; 7 | 8 | import se.jbee.inject.bind.Bootstrapper; 9 | import se.jbee.inject.bind.Bootstrapper.DependentBootstrapper; 10 | import se.jbee.inject.bind.Bundle; 11 | import se.jbee.inject.bind.Dependent; 12 | import se.jbee.inject.bind.InconsistentBinding; 13 | import se.jbee.lang.Type; 14 | 15 | import static se.jbee.lang.Type.raw; 16 | 17 | /** 18 | * The default utility base class for {@link Dependent}s. 19 | * 20 | * @param the type of the options values (usually an enum) 21 | */ 22 | public abstract class BundleFor implements Dependent, 23 | Bootstrapper.DependentBootstrapper { 24 | 25 | private DependentBootstrapper bootstrapper; 26 | 27 | @Override 28 | public void bootstrap(Bootstrapper.DependentBootstrapper bs) { 29 | InconsistentBinding.nonnullThrowsReentranceException(bootstrapper); 30 | this.bootstrapper = bs; 31 | bootstrap(); 32 | } 33 | 34 | @Override 35 | public final void installDependentOn(E element, Class bundle) { 36 | bootstrapper.installDependentOn(element, bundle); 37 | } 38 | 39 | @Override 40 | public final String toString() { 41 | Type module = raw(getClass()).toSuperType(Dependent.class).parameter(0); 42 | return "bundle " + getClass().getSimpleName() + "[" + module + "]"; 43 | } 44 | 45 | /** 46 | * Use {@link DependentBootstrapper#installDependentOn(Object, Class)} for 47 | * option dependent {@link Bundle} installation. 48 | */ 49 | protected abstract void bootstrap(); 50 | } 51 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/scope/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * Contains the default implementations of common {@link se.jbee.inject.Scope}s. 4 | * In the library a scope implementation is a managed container instance. 5 | *

6 | * 7 | *

Defaults

8 | *

9 | * The {@link se.jbee.inject.defaults.DefaultScopes} {@link 10 | * se.jbee.inject.bind.Module} is used to define the common {@link 11 | * se.jbee.inject.Scope} implementations. 12 | *

13 | * 14 | *

Provided Scopes

15 | *
    16 | *
  • {@link se.jbee.inject.scope.ApplicationScope}: A singleton within the 17 | * {@link se.jbee.inject.Injector} context.
  • 18 | *
  • {@link se.jbee.inject.scope.ThreadScope}: A singleton per JVM 19 | * {@link java.lang.Thread}
  • 20 | *
  • {@link se.jbee.inject.scope.WorkerScope}: Base implementation for 21 | * {@link java.lang.Thread} pool based scopes like a request scope in an HTTP 22 | * server.
  • 23 | *
  • {@link se.jbee.inject.scope.DiskScope}: {@link java.io.File} based scope 24 | * for {@link java.io.Serializable} values in a particular directory.
  • 25 | *
  • {@link se.jbee.inject.scope.TypeDependentScope}: Implementation for 26 | * singletons per resolved {@link java.lang.Class}, {@link se.jbee.lang.Type}, 27 | * {@link se.jbee.inject.Instance} or full 28 | * {@link se.jbee.inject.Dependency}.
  • 29 | *
  • {@link se.jbee.inject.scope.SnapshotScope}: A utility 30 | * {@link se.jbee.inject.Scope} that allows to create effective snapshots of 31 | * other {@link se.jbee.inject.Scope} that change asynchronously or concurrently 32 | * to another one, like e.g. a {@link se.jbee.inject.scope.DiskScope}.
  • 33 | *
34 | */ 35 | package se.jbee.inject.scope; 36 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/ActionDispatch.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.action; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * An {@link ActionDispatch} implements the algorithm that is used to 7 | * select and call one or more {@link ActionSite} to produce the {@link Action} 8 | * output. 9 | *

10 | * Different implementations have different strategies to select the {@link 11 | * ActionSite} to use for each particular call and how to handle different types 12 | * of errors. 13 | * 14 | * @param key input value type (parameter type of the {@link Action}) 15 | * @param output value type (return type of the {@link Action}) 16 | * @see RoundRobinDispatch 17 | * @see MulticastDispatch 18 | * @since 8.1 19 | */ 20 | @FunctionalInterface 21 | public interface ActionDispatch { 22 | 23 | /** 24 | * Executes an {@link ActionSite} by using one or more of the available 25 | * {@link ActionSite}s to compute the output value. 26 | * 27 | * @param input the input value 28 | * @param sites a lift of available {@link ActionSite}s, while any of the 29 | * sites might be disconnected at any time the list itself can 30 | * be considered immutable 31 | * @return the output value computed by the used {@link ActionSite} 32 | * @throws se.jbee.inject.DisconnectException When the strategy could not 33 | * compute a result because all 34 | * {@link ActionSite} it tried 35 | * turned out to be disconnected 36 | * in the meantime. 37 | */ 38 | B execute(A input, List> sites); 39 | } 40 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Annotated.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.AnnotatedElement; 5 | import java.lang.reflect.Constructor; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | import java.util.function.UnaryOperator; 9 | 10 | /** 11 | * Can be implemented by {@link Supplier}s to communicate the annotations 12 | * present on the underlying source used to supply instances. 13 | *

14 | * For example a {@link Method}, {@link Constructor} or {@link Field} but also 15 | * any form of user defined source. 16 | * 17 | * @since 8.1 18 | */ 19 | @FunctionalInterface 20 | public interface Annotated { 21 | 22 | /** 23 | * @return the underlying source for annotations 24 | */ 25 | AnnotatedElement element(); 26 | 27 | @FunctionalInterface 28 | interface Enhancer extends UnaryOperator { 29 | } 30 | 31 | /** 32 | * Keeps the annotation as presented by the source. This is how the {@link 33 | * Supplier} presented it from the underlying {@link 34 | * java.lang.reflect.Member} or {@link Class}. 35 | */ 36 | Enhancer SOURCE = a -> a; 37 | 38 | AnnotatedElement NOT_ANNOTATED = new AnnotatedElement() { 39 | 40 | @Override 41 | public T getAnnotation(Class type) { 42 | return null; 43 | } 44 | 45 | @Override 46 | public Annotation[] getAnnotations() { 47 | return new Annotation[0]; 48 | } 49 | 50 | @Override 51 | public Annotation[] getDeclaredAnnotations() { 52 | return new Annotation[0]; 53 | } 54 | 55 | }; 56 | 57 | /** 58 | * NULL object that represents no {@link Annotation}s present. 59 | */ 60 | Annotated EMPTY = () -> NOT_ANNOTATED; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Provider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject; 7 | 8 | import se.jbee.lang.Type; 9 | 10 | import static se.jbee.lang.Type.raw; 11 | 12 | /** 13 | * A indirection that resolves the instance lazily when {@link #provide()} is 14 | * invoked. This is mainly used to allow the injection and usage of instances 15 | * that have a more unstable scope into an instance of a more stable scope. 16 | * 17 | * Usage of {@link Provider}s to circumvent scoping limitations is explicitly 18 | * installed using the {@code CoreFeature#PROVIDER}. 19 | * 20 | * But it is also very easy to use another similar provider interface by 21 | * installing a similar bridge {@link Supplier}. 22 | * 23 | * @author Jan Bernitt (jan@jbee.se) 24 | */ 25 | @FunctionalInterface 26 | public interface Provider { 27 | 28 | static Type> providerTypeOf(Class providedType) { 29 | return providerTypeOf(raw(providedType)); 30 | } 31 | 32 | @SuppressWarnings({ "unchecked", "rawtypes" }) 33 | static Type> providerTypeOf(Type providedType) { 34 | return (Type) raw(Provider.class).parameterized(providedType); 35 | } 36 | 37 | /** 38 | * @return The lazily resolved instance. Implementations should make sure 39 | * that calling this method multiple times does cache the result if 40 | * that is semantically correct. 41 | * @throws UnresolvableDependency in case the underlying implementation 42 | * could not provide the instance due to lack of suitable 43 | * declarations. 44 | */ 45 | T provide() throws UnresolvableDependency; 46 | } 47 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicLoggerBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.binder.Installs; 7 | import se.jbee.inject.bootstrap.Bootstrap; 8 | import se.jbee.inject.defaults.DefaultFeature; 9 | import se.jbee.inject.defaults.DefaultFeatures; 10 | 11 | import java.util.logging.Logger; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertSame; 14 | 15 | /** 16 | * A test that demonstrates how install {@link DefaultFeature#LOGGER} so that each 17 | * class gets its class-specific- {@link Logger} injected. 18 | *

19 | * This is also meant as a template to learn how to implement a similar feature 20 | * for another logging framework. Just have a look at how the {@link 21 | * BinderModule} implementing the {@link DefaultFeature#LOGGER} toggle is 22 | * implemented. 23 | */ 24 | class TestBasicLoggerBinds { 25 | 26 | @Installs(features = DefaultFeature.class, by = DefaultFeatures.class) 27 | @DefaultFeatures(DefaultFeature.LOGGER) 28 | private static class TestBasicLoggerBindsModule extends BinderModule { 29 | 30 | @Override 31 | protected void declare() { 32 | construct(Foo.class); 33 | } 34 | 35 | } 36 | 37 | public static class Foo { 38 | 39 | final Logger logger; 40 | 41 | @SuppressWarnings("unused") 42 | public Foo(Logger logger) { 43 | this.logger = logger; 44 | } 45 | } 46 | 47 | @Test 48 | void eachClassGetsTheLoggerWithItsCanonicalNameInjected() { 49 | Injector context = Bootstrap.injector(TestBasicLoggerBindsModule.class); 50 | Foo foo = context.resolve(Foo.class); 51 | assertSame(Logger.getLogger(Foo.class.getCanonicalName()), foo.logger); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /se.jbee.inject.convert/main/java/se/jbee/inject/convert/Converts.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.convert; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Repeatable; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.*; 9 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 10 | 11 | /** 12 | * Is added to {@link se.jbee.inject.Converter}s to add additional input value 13 | * transformation chains to extend the possible input types of a particular 14 | * {@link se.jbee.inject.Converter}. 15 | */ 16 | @Documented 17 | @Repeatable(ConvertsMultiple.class) 18 | @Retention(RUNTIME) 19 | @Target({ TYPE, METHOD, CONSTRUCTOR, FIELD }) 20 | public @interface Converts { 21 | 22 | /** 23 | * @return A conversion sequence, for example {@code A, B, C} means a value 24 | * of type {@code A} is first converted to {@code B} and from {@code B} to 25 | * {@code C}. The final type (here {@code C}) should be the input type of 26 | * the {@link se.jbee.inject.Converter} this annotation is attached to. 27 | *

28 | * By adding such additional conversion a {@link se.jbee.inject.Converter} 29 | * with an input of {@code C} then can also be have inputs of type {@code A} 30 | * or {@code B} as we know how to get from those to the required input type 31 | * {@code C}. 32 | */ 33 | String[] value(); 34 | 35 | /** 36 | * @return The set of {@link Class} to import so that their {@link 37 | * Class#getSimpleName()} can be used in the conversion sequence given by 38 | * {@link #value()}. Alternativly (or in addition) the {@link Imports} 39 | * annotation can be used to provide the full {@link Class} references for 40 | * simple class names. 41 | */ 42 | Class[] imports() default {}; 43 | } 44 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicScopedBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.Scope; 6 | import se.jbee.inject.UnresolvableDependency.UnstableDependency; 7 | import se.jbee.inject.binder.BinderModule; 8 | import se.jbee.inject.bootstrap.Bootstrap; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertThrows; 12 | 13 | /** 14 | * Very simple demo of the {@link se.jbee.inject.ScopeLifeCycle}s that help to 15 | * detect scoping errors, such as trying to inject an instance in {@link 16 | * Scope#injection} into another one in {@link Scope#application}. 17 | */ 18 | class TestBasicScopedBinds { 19 | 20 | private static class Foo { 21 | 22 | @SuppressWarnings("unused") 23 | Foo(Bar bar) { 24 | // it is just about the instances 25 | } 26 | } 27 | 28 | private static class Bar { 29 | // just to demo 30 | } 31 | 32 | private static class TestBasicScopedBindsModule extends BinderModule { 33 | 34 | @Override 35 | protected void declare() { 36 | per(Scope.application).construct(Foo.class); 37 | per(Scope.injection).construct(Bar.class); 38 | } 39 | } 40 | 41 | private final Injector context = Bootstrap.injector( 42 | TestBasicScopedBindsModule.class); 43 | 44 | @Test 45 | void injectingAnInjectionScopedInstanceIntoAppScopedInstanceThrowsAnException() { 46 | Exception ex = assertThrows(UnstableDependency.class, () -> context.resolve(Foo.class)); 47 | assertEquals("Unstable dependency injection\n" 48 | + "\t of: test.integration.bind.TestBasicScopedBinds.Bar scoped injection\n" 49 | + "\tinto: test.integration.bind.TestBasicScopedBinds.Foo scoped application", 50 | ex.getMessage()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/InstanceLocalBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.Instance; 4 | import se.jbee.inject.Name; 5 | import se.jbee.lang.Type; 6 | 7 | import static se.jbee.inject.Instance.defaultInstanceOf; 8 | import static se.jbee.inject.Name.named; 9 | import static se.jbee.lang.Type.raw; 10 | 11 | /** 12 | * Make a binding local to the injected {@link Instance}. 13 | *

14 | * Usually this is used to specify what parameter to use when injecting the 15 | * target instance. 16 | * 17 | * @since 8.1 18 | * 19 | * @param return type of the binder step following a {@code injectingInto} 20 | * step 21 | */ 22 | @FunctionalInterface 23 | public interface InstanceLocalBinder { 24 | 25 | /** 26 | * Makes all bindings made with the returned binder local to the injection 27 | * of the provided {@link Instance}, in other words when dependencies of 28 | * that {@link Instance} are resolved. 29 | * 30 | * @param target the injected {@link Instance} 31 | * @return next step in fluent API 32 | */ 33 | B injectingInto(Instance target); 34 | 35 | /** 36 | * @see #injectingInto(Instance) 37 | */ 38 | default B injectingInto(String name, Class type) { 39 | return injectingInto(named(name), raw(type)); 40 | } 41 | 42 | /** 43 | * @see #injectingInto(Instance) 44 | */ 45 | default B injectingInto(Name name, Type type) { 46 | return injectingInto(Instance.instance(name, type)); 47 | } 48 | 49 | /** 50 | * @see #injectingInto(Instance) 51 | */ 52 | default B injectingInto(Type target) { 53 | return injectingInto(defaultInstanceOf(target)); 54 | } 55 | 56 | /** 57 | * @see #injectingInto(Instance) 58 | */ 59 | default B injectingInto(Class target) { 60 | return injectingInto(raw(target)); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/InconsistentDeclaration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject; 7 | 8 | import java.lang.annotation.Annotation; 9 | 10 | /** 11 | * If there is a statically resolvable problem with a binding ({@link Locator} 12 | * in the context of a container) this exception is thrown during bootstrapping. 13 | * It is never thrown after the bootstrapping step has finished (a 14 | * {@link Injector} was created successfully). 15 | * 16 | * @see UnresolvableDependency 17 | */ 18 | public class InconsistentDeclaration extends RuntimeException { 19 | 20 | public InconsistentDeclaration(Exception cause) { 21 | super(cause); 22 | } 23 | 24 | public InconsistentDeclaration(String msg, Exception cause) { 25 | super(msg, cause); 26 | } 27 | 28 | public InconsistentDeclaration(String msg) { 29 | super(msg); 30 | } 31 | 32 | // Text should answer: What is the problem with the binding or in the binding process? 33 | 34 | public static InconsistentDeclaration notConstructable(Class impl) { 35 | return new InconsistentDeclaration( 36 | "Attempt to bind a non-constructable type: " + impl); 37 | } 38 | 39 | public static InconsistentDeclaration incomprehensibleHint( 40 | Hint hint) { 41 | return new InconsistentDeclaration( 42 | "Attempt to give a parameter hint that does not fit the target: " 43 | + hint); 44 | } 45 | 46 | public static InconsistentDeclaration annotationLacksProperty( 47 | Class property, Class type) { 48 | return new InconsistentDeclaration("Attempt to use an annotation " 49 | + type.getSimpleName() + " that lacks a expected property of type: " 50 | + property.getSimpleName()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | __.__ 2 | ______ __ _________ ____ |__|__| ____ 3 | \____ \| | \_ __ \_/ __ \ | | |/ \ 4 | | |_> > | /| | \/\ ___/ | | | | \ 5 | | __/|____/ |__| \___ >\__| |__|___| / 6 | |__| \/\______| \/ 7 | 8 | Build with Java 22+, usable with Java 11+ 9 | 10 | Docs http://jbee.github.io/purejin/ 11 | Feedback http://github.com/jbee/purejin/issues 12 | Contribute http://github.com/jbee/purejin/pulls 13 | CI https://github.com/jbee/purejin/actions 14 | Releases https://github.com/jbee/purejin/releases 15 | -------------------------------------------------------------------------- 16 | 17 | Build https://github.com/sormuras/bach 18 | 19 | - Install JDK 22 (or higher) on your machine 20 | - Clone project sources with submodules 21 | - Execute `java @build` 22 | 23 | -------------------------------------------------------------------------- 24 | 25 | License http://www.apache.org/licenses/LICENSE-2.0 26 | 27 | Copyright (c) 2012-2024 Jan Bernitt 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 30 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 39 | THE POSSIBILITY OF SUCH DAMAGE. 40 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestExampleInterfaceDecouplingBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertNotSame; 9 | import static se.jbee.inject.Name.named; 10 | 11 | /** 12 | * This illustrates how to use different named instances for the same interface 13 | * that are all implemented by the same class without having them linked to the 14 | * same instance. 15 | */ 16 | class TestExampleInterfaceDecouplingBinds { 17 | 18 | interface Decoupling { 19 | 20 | } 21 | 22 | public static class DefaultImpl implements Decoupling { 23 | 24 | } 25 | 26 | static class TestExampleInterfaceDecouplingBindsModule extends BinderModule { 27 | 28 | @Override 29 | protected void declare() { 30 | bind(named("a"), Decoupling.class).toConstructor(DefaultImpl.class); 31 | bind(named("b"), Decoupling.class).toConstructor(DefaultImpl.class); 32 | bind(named("c"), Decoupling.class).toConstructor(DefaultImpl.class); 33 | bind(named("d"), Decoupling.class).toConstructor(DefaultImpl.class); 34 | } 35 | } 36 | 37 | @Test 38 | void instancesAreAllDifferentButUseTheSameInterface() { 39 | Injector injector = Bootstrap.injector( 40 | TestExampleInterfaceDecouplingBindsModule.class); 41 | Decoupling a = injector.resolve("a", Decoupling.class); 42 | Decoupling b = injector.resolve("b", Decoupling.class); 43 | Decoupling c = injector.resolve("c", Decoupling.class); 44 | Decoupling d = injector.resolve("d", Decoupling.class); 45 | 46 | assertNotSame(a, b); 47 | assertNotSame(a, c); 48 | assertNotSame(a, d); 49 | assertNotSame(b, c); 50 | assertNotSame(b, d); 51 | assertNotSame(c, d); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestFeaturePackageLocalClassesBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | import se.jbee.inject.config.New; 8 | 9 | import java.lang.reflect.Constructor; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | /** 14 | * Shows the simply but awesome feature of allowing default and package visible 15 | * managed instances created from constructors using reflection without using 16 | * deep reflection ({@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}) 17 | * if adding the following binding to one of the {@link 18 | * se.jbee.inject.bind.Module}s in the same package: 19 | * 20 | *

21 |  * locally().bind(New.class).to(Constructor::newInstance);
22 |  * 
23 | *

24 | * The lambda that is created this way is only used within the package and as it 25 | * is defined in the package will have access to all classes visible in the 26 | * package. 27 | */ 28 | class TestFeaturePackageLocalClassesBinds { 29 | 30 | private static class TestFeaturePackageLocalClassesBindsModule extends 31 | BinderModule { 32 | 33 | @Override 34 | protected void declare() { 35 | locally().bind(New.class).to(Constructor::newInstance); 36 | construct(DefaultVisibleBean.class); 37 | } 38 | } 39 | 40 | static class DefaultVisibleBean { 41 | 42 | DefaultVisibleBean() { 43 | // default visible constructor 44 | } 45 | } 46 | 47 | private final Injector context = Bootstrap.injector( 48 | TestFeaturePackageLocalClassesBindsModule.class); 49 | 50 | @Test 51 | void localNewBindAllowsCreatingDefaultVisibleBeans() { 52 | assertNotNull(context.resolve(DefaultVisibleBean.class)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds a modular Java project with Bach.java 2 | # For more information see: 3 | # - https://help.github.com/en/actions/language-and-framework-guides/github-actions-for-java 4 | # - https://github.com/sormuras/bach 5 | 6 | name: 'CI' 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | branches: [ master ] 13 | 14 | env: 15 | PROJECT_VERSION: '11-ea' 16 | 17 | jobs: 18 | build: 19 | name: "Build (JDK-${{ matrix.java }}, ${{ matrix.os }})" 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | os: [ ubuntu-latest ] 24 | java: [ 22 ] 25 | language: [ 'java' ] 26 | runs-on: ${{ matrix.os }} 27 | steps: 28 | - name: 'Check out sources' 29 | uses: actions/checkout@v4 30 | with: 31 | submodules: recursive 32 | - name: 'Set up JDK' 33 | uses: oracle-actions/setup-java@v1 34 | with: 35 | release: ${{ matrix.java }} 36 | - name: 'Set up version' 37 | run: | 38 | SHA7=$(echo "${{ github.sha }}" | cut -c1-7) 39 | VERSION=${PROJECT_VERSION}+${SHA7} 40 | echo "VERSION=${VERSION}" >> ${GITHUB_ENV} 41 | - name: 'Build with Bach' 42 | run: java --show-version -D--project-version=${VERSION} @build 43 | - name: 'Generate API documentation' 44 | run: java -D--project-version=${VERSION} .bach/src/Document.java 45 | - name: 'Report test summary' 46 | if: always() 47 | uses: test-summary/action@v2 48 | with: 49 | paths: .bach/out/test-reports/**/TEST-*.xml 50 | - name: 'Upload build artifact' 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: ${{ github.event.repository.name }}-build-${{ env.VERSION }} 54 | path: | 55 | .bach/out/main/modules 56 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/example1/TestServiceLoaderBootstrapBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.example1; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Env; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.bind.Bundle; 7 | import se.jbee.inject.binder.ServiceLoaderBundles; 8 | import se.jbee.inject.binder.ServiceLoaderEnvBundles; 9 | import test.Example; 10 | 11 | import java.util.ServiceLoader; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static org.junit.jupiter.api.Assertions.assertNotNull; 15 | 16 | /** 17 | * This test demonstrates how the {@link java.util.ServiceLoader} concept is 18 | * used to define one or more root {@link Bundle} {@link Class}es as 19 | * /META-INF/services/se.jbee.inject.bootstrap.Bundle files in jar 20 | * files to assemble the context of an modular application. 21 | * 22 | * This example is based on the /lib/example.jar which contains 23 | * very basic example of one {@link Bundle} installing one {@link Module} which 24 | * is binding an int and {@link String} value. These example files can be found 25 | * in src/example. 26 | * 27 | * The {@link ServiceLoader} as a source is hooked in explicitly by installing 28 | * the {@link ServiceLoaderEnvBundles} when bootstrapping an {@link Env} and the 29 | * {@link ServiceLoaderBundles} when bootstrapping an {@link Injector}. This 30 | * gives same control as always for this feature as well. 31 | */ 32 | class TestServiceLoaderBootstrapBinds { 33 | 34 | @Test 35 | void serviceLoaderCanBeUsedToDeclareModuleRoots() { 36 | Injector context = Example.EXAMPLE_1.injector(); 37 | assertNotNull(context); 38 | assertEquals(13, context.resolve(int.class).intValue()); 39 | assertEquals("test.example1.Example1Module", 40 | context.resolve(String.class)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestFeatureLiftWithGenericsBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | 11 | import static java.util.Arrays.asList; 12 | import static java.util.Collections.singletonList; 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static se.jbee.lang.Cast.listTypeOf; 15 | 16 | class TestFeatureLiftWithGenericsBinds { 17 | 18 | public static class StringList extends ArrayList { 19 | } 20 | 21 | public static class IntegerList extends ArrayList { 22 | 23 | } 24 | 25 | static class TestFeatureLiftWithGenericsBindsModule extends BinderModule { 26 | 27 | @Override 28 | protected void declare() { 29 | lift(listTypeOf(String.class)).to((target, as, context) -> { 30 | target.add("a"); 31 | return target; 32 | }); 33 | lift(listTypeOf(Integer.class)).to((target, as, context) -> { 34 | target.add(1); 35 | return target; 36 | }); 37 | lift(StringList.class).to((target, as, context) -> { 38 | target.add("b"); 39 | return target; 40 | }); 41 | 42 | // construct the base values 43 | bind(listTypeOf(String.class)).to(StringList.class); 44 | bind(listTypeOf(Integer.class)).to(IntegerList.class); 45 | } 46 | 47 | } 48 | 49 | private final Injector context = Bootstrap.injector( 50 | TestFeatureLiftWithGenericsBindsModule.class); 51 | 52 | @Test 53 | void liftOnlyAffectsActualTypesThatAreAssignable() { 54 | assertEquals(new HashSet<>(asList("b", "a")), 55 | new HashSet<>(context.resolve(listTypeOf(String.class)))); 56 | assertEquals(singletonList(1), context.resolve(listTypeOf(Integer.class))); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /se.jbee.inject.convert/main/java/se/jbee/inject/convert/ConverterModule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.convert; 2 | 3 | import se.jbee.inject.Converter; 4 | import se.jbee.inject.binder.BinderModule; 5 | import se.jbee.inject.config.AccessesBy; 6 | import se.jbee.inject.config.NamesBy; 7 | import se.jbee.inject.config.ProducesBy; 8 | import se.jbee.inject.config.ScopesBy; 9 | import se.jbee.lang.Type; 10 | 11 | import static se.jbee.lang.Type.classType; 12 | 13 | /** 14 | * Base class for {@link se.jbee.inject.bind.Module}s that are used to bind 15 | * {@link Converter}s. 16 | *

17 | * By default it is assumed that the converters are defined as fields or methods 18 | * within the class extending the {@link ConverterModule}. 19 | *

20 | * Alternatively this base class can be used by overriding {@link #declare()} 21 | * and calling {@link #autobindConverters()} with other target types. 22 | *

23 | * Last but not least this can just act as a manual on how to bind converters 24 | * using {@link #autobind()} or other manual binds. 25 | * 26 | * @since 8.1 27 | */ 28 | public abstract class ConverterModule extends BinderModule { 29 | 30 | @SuppressWarnings("rawtypes") 31 | private static final Type ANY_CONVERTER_TYPE = classType( 32 | Converter.class); 33 | 34 | private static final ProducesBy PRODUCES_BY = ProducesBy.OPTIMISTIC // 35 | .returnTypeAssignableTo(ANY_CONVERTER_TYPE); 36 | private static final AccessesBy SHARES_BY = AccessesBy.declaredFields(false) // 37 | .typeAssignableTo(ANY_CONVERTER_TYPE); 38 | 39 | @Override 40 | protected void declare() { 41 | autobindConverters().in(this); 42 | } 43 | 44 | protected final AutoBinder autobindConverters() { 45 | return autobind() // 46 | .nameBy(NamesBy.DECLARED_NAME) // 47 | .scopeBy(ScopesBy.RETURN_TYPE) // 48 | .accessBy(SHARES_BY) // 49 | .produceBy(PRODUCES_BY); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Generator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject; 7 | 8 | import se.jbee.lang.Type; 9 | 10 | import static se.jbee.lang.Type.raw; 11 | 12 | /** 13 | * A {@link Generator} creates the instance(s) for the generator's 14 | * {@link Resource}. 15 | * 16 | * When binding directly to a {@link Generator} any {@link ScopeLifeCycle} will be 17 | * ineffective since the supplied {@link Generator} will directly be asked to 18 | * {@link #generate(Dependency)} the instance for the dependency. This can be used 19 | * to implement instance management different to the one provided by 20 | * {@link Scope}s or to simply avoid unnecessary indirection or processing. 21 | * 22 | * @since 8.1 23 | */ 24 | @FunctionalInterface 25 | public interface Generator { 26 | 27 | @SuppressWarnings({ "unchecked", "rawtypes" }) 28 | static Type> generatorTypeOf(Type providedType) { 29 | return (Type) raw(Generator.class).parameterized(providedType); 30 | } 31 | 32 | @SuppressWarnings({ "unchecked", "rawtypes" }) 33 | static Type[]> generatorsTypeOf(Type generatedType) { 34 | return (Type) raw(Generator[].class).parameterized(generatedType); 35 | } 36 | 37 | /** 38 | * Yields the instance that satisfies the given {@link Dependency}. 39 | * 40 | * @param dep describes the requested instance and injection situation 41 | * @return the instance to use for the given {@link Dependency} 42 | * @throws UnresolvableDependency in case this {@link Generator} is unable 43 | * to create the requested instance because another instance 44 | * needed to create it could not be resolved. 45 | */ 46 | T generate(Dependency dep) throws UnresolvableDependency; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicSupplierBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Dependency; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.Supplier; 7 | import se.jbee.inject.binder.BinderModule; 8 | import se.jbee.inject.bootstrap.Bootstrap; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | /** 13 | * A very minimal test that shows how to use custom {@link Supplier}s. 14 | *

15 | * A {@link Supplier} is the main "backend" (internal) abstraction to generate 16 | * or yield instances of the type supplied by the {@link Supplier}. Their 17 | * user-facing "frontend" counterpart is the {@link se.jbee.inject.Generator}. 18 | *

19 | * While {@link Supplier}s stay inaccessible for the user of the {@link 20 | * Injector} context the {@link se.jbee.inject.Generator}s that act as an facade 21 | * for their internal {@link Supplier} can be resolved by users like any other 22 | * dependency. 23 | * 24 | * @see TestFeatureResolveResourceBinds 25 | */ 26 | class TestBasicSupplierBinds { 27 | 28 | public static class TestBasicSupplierBindsModule extends BinderModule 29 | implements Supplier { 30 | 31 | @Override 32 | protected void declare() { 33 | bind(String.class).toSupplier(TestBasicSupplierBindsModule.class); 34 | bind(Integer.class).toSupplier((dep, context) -> 42); 35 | } 36 | 37 | @Override 38 | public String supply(Dependency dep, Injector context) { 39 | return "foobar"; 40 | } 41 | } 42 | 43 | private final Injector context = Bootstrap.injector( 44 | TestBasicSupplierBindsModule.class); 45 | 46 | @Test 47 | void supplierFromClassReference() { 48 | assertEquals("foobar", context.resolve(String.class)); 49 | } 50 | 51 | @Test 52 | void supplierFromLambda() { 53 | assertEquals(42, context.resolve(Integer.class)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/defaults/ExtensionModule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.defaults; 2 | 3 | import se.jbee.inject.*; 4 | import se.jbee.inject.binder.BinderModule; 5 | import se.jbee.inject.binder.Supply; 6 | import se.jbee.inject.config.ConstructsBy; 7 | import se.jbee.inject.config.Extension; 8 | import se.jbee.lang.Type; 9 | 10 | import java.lang.reflect.Constructor; 11 | 12 | import static se.jbee.inject.binder.Constructs.constructs; 13 | import static se.jbee.lang.Type.raw; 14 | 15 | /** 16 | * Provides a {@link Supplier} that can resolve all types extending an 17 | * {@link Extension} should they not be bound otherwise. 18 | * 19 | * This is the basis of the {@link Extension} functionality where any type 20 | * implementing the abstraction is constructed by that {@link Supplier}. 21 | * 22 | * The created instance is an effective singleton per type, so there will be one 23 | * instance for each {@link Extension} implementation class. 24 | * 25 | * @since 8.1 26 | */ 27 | // intentionally made default visible to not be confused with a module that is useful as a base class for user modules 28 | class ExtensionModule extends BinderModule { 29 | 30 | @Override 31 | protected void declare() { 32 | asDefault() // 33 | .per(Scope.dependencyType) // 34 | .bind(raw(Extension.class).asUpperBound()) // 35 | .toSupplier(ExtensionModule::extension); 36 | } 37 | 38 | @SuppressWarnings({"unchecked", "rawtypes"}) 39 | private static T extension(Dependency dep, Injector context) { 40 | Type expectedType = dep.type(); 41 | Env env = context.resolve(Env.class).in(expectedType.rawType); 42 | ConstructsBy constructsBy = env.property(ConstructsBy.class); 43 | Constructor ext = constructsBy.reflect( 44 | expectedType.rawType.getDeclaredConstructors()); 45 | return (T) Supply.byConstruction(constructs(expectedType, ext, env)) // 46 | .supply((Dependency) dep, context); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/event/EventModule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.event; 2 | 3 | import se.jbee.inject.binder.BinderModule; 4 | import se.jbee.inject.binder.spi.ConnectorBinder; 5 | import se.jbee.inject.config.Connector; 6 | 7 | import java.util.function.Consumer; 8 | 9 | import static se.jbee.inject.event.EventTrigger.eventTriggerTypeOf; 10 | 11 | public abstract class EventModule extends BinderModule { 12 | 13 | protected EventModule() { 14 | super(EventBaseModule.class); 15 | } 16 | 17 | /** 18 | * Responsible for setting up the basics of event feature. 19 | */ 20 | public static final class EventBaseModule extends BinderModule { 21 | 22 | @Override 23 | protected void declare() { 24 | // have the dispatcher receive "event" type method connections 25 | asDefault().bind(ConnectorBinder.EVENT_CONNECTOR, Connector.class) 26 | .to(DefaultEventDispatcher.class); 27 | 28 | // connect On 29 | receiveIn(On.Aware.class, On.class); 30 | //TODO should read: in(On.Aware.class).receive(On.class); 31 | // or: receive(On.class).in(On.Aware.class); 32 | 33 | // hook Shutdown into Runtime 34 | asDefault().bind(eventTriggerTypeOf(On.Shutdown.class)) // 35 | .to(EventBaseModule::activateOnShutdown); 36 | } 37 | 38 | private static void activateOnShutdown(Consumer dispatcher) { 39 | Runtime.getRuntime().addShutdownHook(new Thread( 40 | () -> dispatcher.accept(new On.Shutdown()))); 41 | } 42 | } 43 | 44 | // use events to process schedules 45 | // scheduler is just an event trigger 46 | // add Run class to Schedule which is the event to run a scheduled method 47 | // when a schedule is connected we also connect it as an event 48 | // all events use a special dispatcher: ScheduledDispatch which uses a target ID to pick the richtig EventTarget/EventSite/Receiver by this ID (full method name) 49 | // this means scheduled methods can also return events they want to trigger 50 | } 51 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/schedule/Schedule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.schedule; 2 | 3 | import se.jbee.inject.Injector; 4 | import se.jbee.inject.event.EventTarget; 5 | import se.jbee.lang.Type; 6 | 7 | import java.lang.reflect.Method; 8 | import java.time.Duration; 9 | import java.time.LocalDateTime; 10 | 11 | public final class Schedule { 12 | 13 | public static class Run { 14 | 15 | public final EventTarget target; 16 | 17 | public Run(EventTarget target) { 18 | this.target = target; 19 | } 20 | } 21 | 22 | @FunctionalInterface 23 | public interface ScheduleFactory { 24 | 25 | Schedule create(Object instance, Type as, Method scheduled, Injector context); 26 | 27 | } 28 | 29 | public final Object instance; 30 | public final Type as; 31 | public final Method scheduled; 32 | public final Duration interval; 33 | public final LocalDateTime firstRun; 34 | public final int cancelAfterConsecutiveFailedRuns; 35 | 36 | //TODO maybe rather have callbacks like: onRunFailed(Exception ex, ExecutionControl control); where control allows to e.g. cancel the execution and such things 37 | 38 | public Schedule(Object instance, Type as, Method scheduled, 39 | Duration interval, LocalDateTime firstRun, int cancelAfterConsecutiveFailedRuns) { 40 | this.instance = instance; 41 | this.as = as; 42 | this.scheduled = scheduled; 43 | this.interval = interval; 44 | this.firstRun = firstRun; 45 | this.cancelAfterConsecutiveFailedRuns = cancelAfterConsecutiveFailedRuns; 46 | } 47 | 48 | public Duration delayNow() { 49 | Duration delay = Duration.between(LocalDateTime.now(), firstRun); 50 | return delay.isNegative() ? Duration.ZERO : delay; 51 | } 52 | 53 | public boolean cancelAfterFailedRuns() { 54 | return cancelAfterConsecutiveFailedRuns > 0; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return String.format("%s[%s] every %s from %s", 60 | getClass().getSimpleName(), scheduled, interval, firstRun); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/example1/TestCustomAnnotationBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.example1; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Env; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.bind.ModuleWith; 7 | import se.jbee.inject.binder.BinderModule; 8 | import se.jbee.inject.bootstrap.Bootstrap; 9 | import test.Example; 10 | import test.example1.Support; 11 | 12 | import java.util.ServiceLoader; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertNotNull; 15 | 16 | /** 17 | * This example shows that custom annotations can be provided via 18 | * {@link ServiceLoader} using {@link ModuleWith} as contract. This has the 19 | * benefit of plugging into the bootstrapping without additional setup code but 20 | * has the drawback that the class implementing the annotation effect cannot 21 | * have constructor arguments. 22 | */ 23 | class TestCustomAnnotationBinds { 24 | 25 | // annotation and its effect is defined in the com.example.app test dependency 26 | 27 | @Support 28 | public static class MySupportService { 29 | 30 | } 31 | 32 | static class TestCustomAnnotationBindsModule extends BinderModule { 33 | 34 | @Override 35 | protected void declare() { 36 | detectAt(MySupportService.class); 37 | } 38 | } 39 | 40 | /** 41 | * This verifies that the {@link Support} annotation's effect is loaded via 42 | * {@link ServiceLoader}. It is defined in the com.example.app test 43 | * dependency jar file. If the {@link MySupportService} can be resolved it 44 | * was bound which meant the annotation had done its effect. Otherwise no 45 | * binding would exist for the class. 46 | */ 47 | @Test 48 | void customAnnotationsAddedViaServiceLoader() { 49 | Env env = Example.EXAMPLE_1.env(); 50 | Injector injector = Bootstrap.injector(env, 51 | TestCustomAnnotationBindsModule.class); 52 | MySupportService service = injector.resolve(MySupportService.class); 53 | assertNotNull(service); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/ContextAware.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | /** 4 | * {@link ContextAware} is a callback or hook invoked each time an instance of 5 | * the implementing class is injected or resolved allowing the implementation 6 | * class to return a modified instance that is adopted to the context. 7 | * 8 | * While this could be used to manipulate the instance state in place it is very 9 | * unlikely that this is correct as injections in other places and threads would 10 | * cause such in place modifications. Instead implementations should return 11 | * instances with modified state that reflects the adaptation to the 12 | * {@link Dependency} context. 13 | * 14 | * In contrast to an {@link Lift} the {@link ContextAware} is not called 15 | * on construction but each time the instance is injected (resolved) by the 16 | * {@link Injector}. This is limited to scoped {@link Resource}s. This are 17 | * {@link Resource}s that neither are in {@link Scope} 18 | * {@link Scope#container} or {@link Scope#reference} and which do not directly 19 | * generate their instances by having the {@link Supplier} also implement 20 | * {@link Generator}. Such {@link Resource}s are not 21 | * {@link ContextAware} by default. 22 | * 23 | * @since 8.1 24 | * 25 | * @param type of the instance being resolved which itself is an instance of 26 | * {@link ContextAware}. This should be the same type as the class 27 | * implementing the {@link ContextAware} interface. This is a 28 | * self-referential type construct similar to {@link Enum}. 29 | */ 30 | @FunctionalInterface 31 | public interface ContextAware { 32 | 33 | /** 34 | * Returns this instance adopted to the provided context. 35 | * 36 | * @param context the {@link Dependency} currently resolved/injected which 37 | * is the context to adapt to 38 | * @return This instance adopted to the context 39 | */ 40 | T inContext(Dependency context); 41 | } 42 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/BinderModuleWith.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.binder; 7 | 8 | import se.jbee.inject.Env; 9 | import se.jbee.inject.bind.Bindings; 10 | import se.jbee.inject.bind.Bootstrapper; 11 | import se.jbee.inject.bind.Bundle; 12 | import se.jbee.inject.bind.ModuleWith; 13 | import se.jbee.lang.Type; 14 | 15 | import java.lang.annotation.Annotation; 16 | 17 | import static se.jbee.lang.Type.raw; 18 | 19 | /** 20 | * The default utility {@link ModuleWith}. 21 | * 22 | * A {@link BinderModuleWith} is also a {@link Bundle} so it should be used and 23 | * installed as such. It will than {@link Bundle#bootstrap(Bootstrapper)} itself 24 | * as a module. 25 | */ 26 | public abstract class BinderModuleWith extends AbstractBinderModule 27 | implements ModuleWith { 28 | 29 | @Override 30 | public final void bootstrap(Bootstrapper bootstrap) { 31 | bootstrap.installDefaults(); 32 | bootstrap.install(this); 33 | installAnnotated(getClass(), bootstrap); 34 | } 35 | 36 | @Override 37 | public final void declare(Bindings bindings, Env env, T property) { 38 | __init__(configure(env.withIsolate()), bindings); 39 | declare(property); 40 | } 41 | 42 | protected Env configure(Env env) { 43 | return env; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | Type arg = raw(getClass()).toSuperType(ModuleWith.class).parameter(0); 49 | return "module " + getClass().getSimpleName() + "[" + arg + "]"; 50 | } 51 | 52 | /** 53 | * @see ModuleWith#declare(Bindings, Env, Object) 54 | * 55 | * @param property The value contained from the {@link Env} by type or the 56 | * annotated {@link Class} in case this {@link ModuleWith} 57 | * defines the effects of a custom {@link Annotation}. 58 | */ 59 | protected abstract void declare(T property); 60 | } 61 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/convert/TestConvertTo.java: -------------------------------------------------------------------------------- 1 | package test.integration.convert; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Converter; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | import se.jbee.inject.convert.ConvertTo; 8 | import se.jbee.inject.convert.ConverterModule; 9 | import se.jbee.inject.convert.Converts; 10 | import se.jbee.inject.convert.Imports; 11 | 12 | import java.math.BigInteger; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; 16 | import static se.jbee.lang.Type.raw; 17 | 18 | class TestConvertTo { 19 | 20 | @Imports({ String.class, Integer.class }) 21 | @Converts({ "String", "Long", "Integer" }) 22 | public static final class ExampleConverter 23 | implements Converter { 24 | 25 | @Override 26 | public BigInteger convert(Integer input) { 27 | return BigInteger.valueOf(input.longValue()); 28 | } 29 | } 30 | 31 | public static class TestChainModule extends ConverterModule { 32 | 33 | public Converter str2long = Long::parseLong; 34 | public Converter long2int = Long::intValue; 35 | 36 | } 37 | 38 | private final Injector context = Bootstrap.injector(TestChainModule.class); 39 | 40 | @Test 41 | void chainCanBeStartedFromAnyLinksInput() { 42 | ConvertTo toBigInteger = new ConvertTo<>(new ExampleConverter(), context); 43 | assertConverts(toBigInteger, String.class, "42", BigInteger.valueOf(42)); 44 | assertConverts(toBigInteger, Integer.class, 42, BigInteger.valueOf(42)); 45 | assertConverts(toBigInteger, Long.class, 42L, BigInteger.valueOf(42)); 46 | } 47 | 48 | private static void assertConverts(ConvertTo convertTo, Class type, 49 | A input, B expected) { 50 | Converter a2b = convertTo.from(raw(type)); 51 | assertNotNull(a2b); 52 | assertEquals(expected, a2b.convert(input)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/convert/TestCollectionConverter.java: -------------------------------------------------------------------------------- 1 | package test.integration.convert; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Converter; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | import se.jbee.inject.convert.ConverterModule; 8 | import se.jbee.lang.Type; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | import static java.util.Arrays.asList; 14 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 15 | import static org.junit.jupiter.api.Assertions.assertEquals; 16 | import static se.jbee.lang.Cast.listTypeOf; 17 | import static se.jbee.lang.Type.raw; 18 | import static se.jbee.lang.Utils.arrayMap; 19 | 20 | class TestCollectionConverter { 21 | 22 | public static class TestCollectionConverterModule extends ConverterModule { 23 | 24 | public static final Converter str2int = Integer::parseInt; 25 | public static final Converter> arr2list = Arrays::asList; 26 | 27 | public static Converter toArray(Type elementType, 28 | Converter elementConverter) { 29 | return in -> arrayMap(in.split(","), elementType.rawType, 30 | elementConverter::convert); 31 | } 32 | } 33 | 34 | private final Injector context = Bootstrap.injector( 35 | TestCollectionConverterModule.class); 36 | 37 | @Test 38 | void genericArrayToCollectionConverterCanBeDefined() { 39 | Converter> arr2list = context.resolve( 40 | Converter.converterTypeOf(raw(Number[].class), listTypeOf(Number.class))); 41 | assertEquals(asList(1, 2), arr2list.convert(new Number[] { 1, 2 })); 42 | } 43 | 44 | @Test 45 | void genericStringToArrayConverterCanBeDefined() { 46 | Converter str2intArr = context.resolve( 47 | Converter.converterTypeOf(String.class, Integer[].class)); 48 | assertArrayEquals(new Integer[] { 42, 13 }, 49 | str2intArr.convert("42,13")); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestFeatureInstallsAnnotationBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.binder.Installs; 7 | import se.jbee.inject.bootstrap.Bootstrap; 8 | 9 | import static se.jbee.junit.assertion.Assertions.assertEqualsIgnoreOrder; 10 | 11 | /** 12 | * Just a short example that shows that the {@link Installs} annotation while 13 | * being inherited is not effectively inherited when another {@link Installs} 14 | * annotation is present on a subtype. In such case the annotation is overridden 15 | * by that instance of the annotation. 16 | *

17 | * Hence, {@link Installs} should only be used on leaf classes that are not 18 | * subject to further inheritance. To install a {@link se.jbee.inject.bind.Bundle} 19 | * as a "side-effect" of a {@link se.jbee.inject.bind.Module} use the {@link 20 | * BinderModule#BinderModule(Class)} constructor. 21 | */ 22 | class TestFeatureInstallsAnnotationBinds { 23 | 24 | @Installs(bundles = A.class) 25 | abstract static class RootModule extends BinderModule { 26 | 27 | } 28 | 29 | @Installs(bundles = B.class) 30 | abstract static class NodeModule extends RootModule { 31 | 32 | } 33 | 34 | static class LeafModule extends NodeModule { 35 | 36 | @Override 37 | protected void declare() { 38 | multibind(String.class).to("C"); 39 | } 40 | } 41 | 42 | static class A extends BinderModule { 43 | 44 | @Override 45 | protected void declare() { 46 | multibind(String.class).to("A"); 47 | } 48 | } 49 | 50 | static class B extends BinderModule { 51 | 52 | @Override 53 | protected void declare() { 54 | multibind(String.class).to("B"); 55 | } 56 | } 57 | 58 | private final Injector context = Bootstrap.injector(LeafModule.class); 59 | 60 | @Test 61 | void installsOnSupertypesAreOverridden() { 62 | assertEqualsIgnoreOrder(new String[] { "B", "C" }, 63 | context.resolve(String[].class)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/bind/ModuleWith.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.bind; 7 | 8 | import se.jbee.inject.Env; 9 | import se.jbee.inject.Name; 10 | import se.jbee.lang.Type; 11 | 12 | import java.lang.annotation.Annotation; 13 | import java.lang.reflect.Method; 14 | 15 | import static se.jbee.lang.Type.raw; 16 | 17 | /** 18 | * A {@link ModuleWith} is an extension to a usual {@link Module} that depends 19 | * on *one* of the values that have been set in the {@link Env}. The property is 20 | * resolved by type. 21 | * 22 | * {@link ModuleWith} are also used to apply the effects of custom 23 | * {@link Annotation}s. In that case the property passed is the annotated 24 | * {@link Class}. 25 | * 26 | * @see Module 27 | * 28 | * @param The type of the property value 29 | */ 30 | @FunctionalInterface 31 | public interface ModuleWith extends Module { 32 | 33 | @SuppressWarnings({ "unchecked", "rawtypes" }) 34 | Type>> TYPE_ANNOTATION = // 35 | (Type) raw(ModuleWith.class).parameterized(Type.CLASS); 36 | 37 | @SuppressWarnings({ "unchecked", "rawtypes" }) 38 | Type> METHOD_ANNOTATION = // 39 | (Type) raw(ModuleWith.class).parameterized(Method.class); 40 | 41 | /** 42 | * @param bindings use to declare made bound within this {@link Module}. 43 | * @param property The set value for the type (maybe null in case no value 44 | * was set or the value was set to null) 45 | */ 46 | void declare(Bindings bindings, Env env, T property); 47 | 48 | @Override 49 | default void declare(Bindings bindings, Env env) { 50 | Type valueType = raw(getClass()).toSuperType(ModuleWith.class).parameter(0); 51 | @SuppressWarnings("unchecked") 52 | final T value = valueType.rawType == Env.class 53 | ? (T) env 54 | : (T) env.property(Name.DEFAULT, valueType, 55 | getClass()); 56 | declare(bindings, env, value); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/Edition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.config; 7 | 8 | import se.jbee.inject.Packages; 9 | import se.jbee.lang.Type; 10 | 11 | import java.lang.annotation.Annotation; 12 | import java.util.EnumSet; 13 | 14 | /** 15 | * An {@link Edition} decides which features are contained in a specific setup. 16 | * 17 | * The particular mechanism how a {@link Edition} decides an the basis of a 18 | * {@link se.jbee.inject.bind.Bundle} or {@link se.jbee.inject.bind.Module} {@link Class} reference if it is 19 | * {@link #featured(Class)} is abstract and can be implemented in many ways. 20 | * 21 | * Common ways are to utilise type level {@link Annotation}s to indicate which 22 | * feature a {@link Class} represents and {@link Enum}s to decide which features 23 | * should be included. 24 | * 25 | * @author Jan Bernitt (jan@jbee.se) 26 | */ 27 | @FunctionalInterface 28 | public interface Edition { 29 | 30 | /** 31 | * Default {@link Edition} that has all features. Or in other words will 32 | * install all {@link se.jbee.inject.bind.Bundle}s and {@link se.jbee.inject.bind.Module}s. 33 | */ 34 | Edition FULL = bundleOrModule -> true; 35 | 36 | /** 37 | * @return true if the given {@link Class} of a module or bundle should be 38 | * included in the context created (should be installed). 39 | */ 40 | boolean featured(Class bundleOrModule); 41 | 42 | static Edition includes(Packages included) { 43 | return bundleOrModule -> included.contains(Type.raw(bundleOrModule)); 44 | } 45 | 46 | @SafeVarargs 47 | static & Feature> Edition includes(F... featured) { 48 | if (featured.length == 0) { 49 | return bundleOrModule -> false; 50 | } 51 | final EnumSet set = EnumSet.of(featured[0], featured); 52 | return bundleOrModule -> { 53 | F f = featured[0].featureOf(bundleOrModule); 54 | return f == null || set.contains(f); 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /se.jbee.inject.api/main/java/se/jbee/inject/Extends.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject; 2 | 3 | import java.lang.annotation.Target; 4 | import java.lang.annotation.*; 5 | import java.util.ServiceLoader; 6 | 7 | import static java.lang.annotation.ElementType.TYPE; 8 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 | 10 | /** 11 | * {@link Annotation} used in connection with {@link ServiceLoader} mechanism to 12 | * annotate service classes to point out the role they implement in cases where 13 | * this is ambiguous for the service interface they implement. 14 | * 15 | *

Usages

16 | * 17 | *

Providing Annotation Templet via ServiceLoader

18 | *

19 | * Annotate a {@code se.jbee.inject.bind.ModuleWith} implementation that should 20 | * load via {@link ServiceLoader} to implement the effects of an type level 21 | * {@link Annotation} and have {@link Extends#value()} point to the 22 | * {@link Annotation} type that triggers the implementation 23 | * {@code se.jbee.inject.bind.ModuleWith}. If the provided {@link Class} is not 24 | * an {@link Annotation} type the module is ignored. 25 | *

26 | * 27 | *

Providing Bundles via ServiceLoader

28 | *

29 | * Annotate a {@code se.jbee.inject.bind.Bundle} implementation that should load 30 | * via {@link ServiceLoader} have {@link Extends#value()} be {@link Env} 31 | * {@link Class} to add the bundle to those that should be loaded as part of the 32 | * {@link Env} instead of the {@link Injector} context. To add 33 | * {@code se.jbee.inject.bind.Bundle}s to the {@link Injector} context don't 34 | * annotate them with {@link Extends} or use {@link Injector} {@link Class} as 35 | * {@link Extends#value()}. 36 | *

37 | * 38 | * @since 8.1 39 | */ 40 | @Inherited 41 | @Documented 42 | @Retention(RUNTIME) 43 | @Target(TYPE) 44 | public @interface Extends { 45 | 46 | /** 47 | * @return refers to a type in the context of the annotated extension. The 48 | * semantic depends on the annotated type. See class level 49 | * documentation for details. 50 | */ 51 | Class value(); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/ParentLocalBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.Instance; 4 | import se.jbee.inject.Name; 5 | import se.jbee.lang.Type; 6 | 7 | import static se.jbee.inject.Instance.anyOf; 8 | import static se.jbee.inject.Instance.instance; 9 | import static se.jbee.inject.Name.named; 10 | import static se.jbee.lang.Type.raw; 11 | 12 | /** 13 | * Make a binding local to the parent {@link Instance} of the currently injected 14 | * bean. 15 | * 16 | * Multiple calls to {@link #within(Instance)} (and its variants) can be used 17 | * to make the binding to a parent hierarchy. 18 | * 19 | * @since 8.1 20 | * 21 | * @param return type of the binder step following a {@code within} step 22 | */ 23 | @FunctionalInterface 24 | public interface ParentLocalBinder { 25 | 26 | /** 27 | * Makes all bindings made with the returned binder local to the provided 28 | * parent instance. 29 | * 30 | * @param parent the parent {@link Instance} to match 31 | * @return next step in fluent API 32 | */ 33 | B within(Instance parent); 34 | 35 | /** 36 | * @see #within(Instance) 37 | */ 38 | default B within(String name, Class parent) { 39 | return within(instance(named(name), raw(parent))); 40 | } 41 | 42 | /** 43 | * @see #within(Instance) 44 | */ 45 | default B within(Name name, Type parent) { 46 | return within(instance(name, parent)); 47 | } 48 | 49 | /** 50 | * Makes all bindings made with the returned binder local to any parent 51 | * having the provided type. 52 | * 53 | * @param parent the type of parent instances to match 54 | * @return next step in fluent API 55 | */ 56 | default B within(Class parent) { 57 | return within(raw(parent)); 58 | } 59 | 60 | /** 61 | * Makes all bindings made with the returned binder local to any parent 62 | * having the provided type. 63 | * 64 | * @param parent the type of parent instances to match 65 | * @return next step in fluent API 66 | */ 67 | default B within(Type parent) { 68 | return within(anyOf(parent)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /se.jbee.inject.bootstrap/main/java/se/jbee/inject/defaults/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains {@link se.jbee.inject.bind.Bundle}s and {@link 3 | * se.jbee.inject.bind.Module}s that declare the {@link 4 | * se.jbee.inject.defaults.DefaultFeature}s of the library that are build in top of 5 | * the general container mechanism itself. 6 | * 7 | *

Defaults

8 | *

9 | * This also includes default implementations for {@link 10 | * se.jbee.inject.bind.ValueBinder}s (see {@link se.jbee.inject.defaults.DefaultValueBinders}) 11 | * and the {@link se.jbee.inject.Supplier} implementations they use (see {@link 12 | * se.jbee.inject.binder.Supply}) as well as source value types expended by the 13 | * default {@link se.jbee.inject.bind.ValueBinder}s: 14 | *

15 | *
    16 | *
  • {@link se.jbee.inject.binder.Constructs}
  • 17 | *
  • {@link se.jbee.inject.binder.Constant}
  • 18 | *
  • {@link se.jbee.inject.binder.Produces}
  • 19 | *
20 | *

21 | * The base classes mentioned in usage section all install the 22 | * {@link se.jbee.inject.defaults.DefaultsBundle} which binds defaults for 23 | * {@link se.jbee.inject.Scope}s, {@link se.jbee.inject.AnnotatedWith} and 24 | * {@link se.jbee.inject.config.Extension}. As always this can be uninstalled or 25 | * overridden using explicit binds. 26 | *

27 | * 28 | *

Utilities

29 | *

30 | * The {@link se.jbee.inject.defaults.AnnotatedWithModule} provides the default 31 | * implementation of the {@link se.jbee.inject.AnnotatedWith} utility type. 32 | *

33 | *

34 | * The {@link se.jbee.inject.defaults.ExtensionModule} provides the default 35 | * implementation for {@link se.jbee.inject.config.Extension} concept. 36 | *

37 | *

38 | * The {@link se.jbee.inject.defaults.DefaultFeature} basic adapters that extend 39 | * the range of types available based on other bound instances. 40 | * {@link se.jbee.inject.defaults.DefaultFeature#ENV} and 41 | * {@link se.jbee.inject.defaults.DefaultFeature#SUB_CONTEXT} are installed by 42 | * default. 43 | *

44 | **/ 45 | package se.jbee.inject.defaults; 46 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/schedule/Scheduled.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.schedule; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import static java.lang.annotation.ElementType.METHOD; 9 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 10 | 11 | @Documented 12 | @Retention(RUNTIME) 13 | @Target(METHOD) 14 | public @interface Scheduled { 15 | 16 | /** 17 | * Marker interface to be implemented by managed instances that use the 18 | * {@link Scheduled} annotation to mark methods that should be scheduled. 19 | *

20 | * There is nothing special about this interface except that the {@link 21 | * SchedulerModule} connects it to the {@link Scheduled} annotation so 22 | * instances of implementing classes get connected via {@link 23 | * se.jbee.inject.Lift}. 24 | */ 25 | interface Aware {} 26 | 27 | /** 28 | * @return The {@link TimeUnit} used for the {@link #every()} and {@link #starting()} property. 29 | */ 30 | TimeUnit unit() default TimeUnit.SECONDS; 31 | 32 | /** 33 | * @return length of the scheduled interval in millis, seconds, minutes (depending on {@link #unit()}) 34 | */ 35 | int every() default 1; 36 | 37 | /** 38 | * @return 39 | */ 40 | int starting() default -1; 41 | 42 | /** 43 | * @return name of the {@link se.jbee.inject.config.Config} property in case 44 | * the time is not given directly by {@link #every()} and {@link #unit()}. 45 | *

46 | * The annotated type is used as context for the {@link 47 | * se.jbee.inject.config.Config}. 48 | *

49 | * If this property is defined the time defined by {@link #every()} and 50 | * {@link #unit()} acts as a default or fallback in case the configuration 51 | * is not defined. 52 | */ 53 | String by() default ""; 54 | 55 | /** 56 | * @return maximum number of consecutive execution failures after which 57 | * scheduling is cancelled. If zero or negative execution is never 58 | * cancelled. 59 | */ 60 | int maxFails() default 1; 61 | } 62 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestBasicMultiInstallBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.binder.BootstrapperBundle; 7 | import se.jbee.inject.bootstrap.Bootstrap; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | /** 12 | * A very small example showing that stateless {@link se.jbee.inject.bind.Module} 13 | * classes can be installed multiple times using different actual instances 14 | * without causing errors because of duplicate (clashing) bindings. 15 | *

16 | * This is because they are recognised as stateless and only the first install 17 | * does have an effect. Further installs are ignored. 18 | *

19 | * The situation would be different if the installed {@link 20 | * se.jbee.inject.bind.Module} has state. In such case is must be assumed that 21 | * this is intentional reuse of the same code to create different bindings based 22 | * on the internal state. Such {@link se.jbee.inject.bind.Module} are all 23 | * installed. Should these lead to clashing bindings this causes the 24 | * bootstrapping to fail with an exception. 25 | */ 26 | class TestBasicMultiInstallBinds { 27 | 28 | private static class TwiceInstalledModule extends BinderModule { 29 | 30 | @Override 31 | protected void declare() { 32 | bind(Integer.class).to(42); 33 | } 34 | 35 | } 36 | 37 | private static class TestMultiInstallBundle extends BootstrapperBundle { 38 | 39 | @Override 40 | protected void bootstrap() { 41 | installDefaults(); 42 | install(new TwiceInstalledModule()); 43 | install(new TwiceInstalledModule()); 44 | } 45 | } 46 | 47 | /** 48 | * A stateless module that just has one initial state (or no state) can be 49 | * installed multiple times without causing an error. 50 | */ 51 | @Test 52 | void statelessModulesCanBeInstalledTwice() { 53 | Injector injector = Bootstrap.injector(TestMultiInstallBundle.class); 54 | assertEquals(42, injector.resolve(Integer.class).intValue()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /se.jbee.inject.contract/main/java/se/jbee/inject/contract/Event.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.contract; 7 | 8 | import se.jbee.lang.Type; 9 | 10 | import java.lang.reflect.Method; 11 | import java.util.function.BinaryOperator; 12 | 13 | import static java.lang.System.currentTimeMillis; 14 | 15 | /** 16 | * The message describing the handler interface method invocation as data. 17 | * 18 | * @since 8.1 19 | * 20 | * @param type of the event handler interface 21 | * @param return type of the {@link #target} method 22 | */ 23 | public final class Event { 24 | 25 | /** 26 | * The timestamp used to compute if a events TTL has expired or not. 27 | */ 28 | public final long created; 29 | public final Class handlerType; 30 | public final EventPolicy policy; 31 | public final Type result; 32 | public final Method target; 33 | public final Object[] args; 34 | /** 35 | * The function used to aggregate multiple values if a computation is 36 | * dispatched to more then one handler using 37 | * {@link EventPolicy#isAggregatedMultiDispatch()}. 38 | */ 39 | public final BinaryOperator aggregator; 40 | 41 | public Event(Class handlerType, EventPolicy policy, Type result, 42 | Method target, Object[] args, BinaryOperator aggregator) { 43 | this.handlerType = handlerType; 44 | this.policy = policy; 45 | this.result = result; 46 | this.target = target; 47 | this.args = args; 48 | this.aggregator = aggregator; 49 | this.created = currentTimeMillis(); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "[" + handlerType.getSimpleName() + "]:" + target.getName(); 55 | } 56 | 57 | public boolean isNonConcurrent() { 58 | return policy.maxConcurrency == 1; 59 | } 60 | 61 | public boolean isExpired() { 62 | return policy.ttl > 0 && currentTimeMillis() > created + policy.ttl; 63 | } 64 | 65 | public boolean returnsVoid() { 66 | return result.rawType == void.class || result.rawType == Void.class; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestExampleAnnotatedScopeBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.*; 5 | import se.jbee.inject.binder.BinderModule; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | import se.jbee.inject.config.ScopesBy; 8 | import test.integration.util.Scoped; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotSame; 12 | 13 | /** 14 | * This test show how to override the default {@link ScopesBy} in the {@link 15 | * Env} to allow explicitly setting the {@link Scope} of bound instances using 16 | * {@link java.lang.annotation.Annotation}s. 17 | *

18 | * This build upon that the default {@link Scope} is {@link Scope#auto} which is 19 | * a "virtual" scope indicating that {@link ScopesBy} should be used to 20 | * determine the used {@link Scope}. Should a binding explicitly set a {@link 21 | * Scope} using {@link se.jbee.inject.binder.Binder.RootBinder#per(Name)} this 22 | * still is honoured and the custom {@link ScopesBy} strategy is not used for 23 | * such a binding. 24 | */ 25 | class TestExampleAnnotatedScopeBinds { 26 | 27 | private static class TestExampleAnnotatedScopeBindsModule extends BinderModule { 28 | 29 | @Override 30 | public Env configure(Env env) { 31 | return env.with(ScopesBy.class, // 32 | ScopesBy.annotatedWith(Scoped.class, Scoped::value)); 33 | } 34 | 35 | @Override 36 | protected void declare() { 37 | bind(InjectionScoped.class).toConstructor(); 38 | } 39 | } 40 | 41 | @Scoped("injection") 42 | public static final class InjectionScoped { 43 | 44 | } 45 | 46 | private final Injector injector = Bootstrap.injector( 47 | TestExampleAnnotatedScopeBindsModule.class); 48 | 49 | @Test 50 | void annotatedScopeIsUsed() { 51 | assertNotSame(injector.resolve(InjectionScoped.class), 52 | injector.resolve(InjectionScoped.class)); 53 | Resource resource = injector.resolve( 54 | Resource.resourceTypeOf(InjectionScoped.class)); 55 | assertEquals(Scope.injection, resource.lifeCycle.scope); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestExampleDependencyAsHintBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Dependency; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.Instance; 7 | import se.jbee.inject.binder.BinderModule; 8 | import se.jbee.inject.binder.Installs; 9 | import se.jbee.inject.bootstrap.Bootstrap; 10 | import se.jbee.inject.defaults.DefaultFeature; 11 | import se.jbee.inject.defaults.DefaultFeatures; 12 | 13 | import java.util.logging.Logger; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertSame; 16 | import static se.jbee.inject.Dependency.dependency; 17 | 18 | /** 19 | * This test demonstrates the most powerful {@link se.jbee.inject.Hint}: a 20 | * {@link Dependency}. 21 | * 22 | * It allows to also describe what {@link Instance} should be used dependent on 23 | * its parent(s) it would be {@link Dependency#injectingInto(Class)}. Though 24 | * this we can tell to inject the {@link Logger} that would be injected into the 25 | * {@link BinderModule} class into our test object {@link Bean}. 26 | * 27 | * @see TestBasicHintsBinds 28 | */ 29 | class TestExampleDependencyAsHintBinds { 30 | 31 | @Installs(features = DefaultFeature.class, by = DefaultFeatures.class) 32 | @DefaultFeatures(DefaultFeature.LOGGER) 33 | private static class TestExampleDependencyAsHintBindsModule 34 | extends BinderModule { 35 | 36 | @Override 37 | protected void declare() { 38 | bind(Bean.class).toConstructor( 39 | dependency(Logger.class).injectingInto(BinderModule.class).asHint()); 40 | } 41 | } 42 | 43 | public static class Bean { 44 | 45 | final Logger logger; 46 | 47 | @SuppressWarnings("unused") 48 | public Bean(Logger logger) { 49 | this.logger = logger; 50 | } 51 | } 52 | 53 | @Test 54 | void dependencyHintAffectsInjection() { 55 | Injector resolver = Bootstrap.injector( 56 | TestExampleDependencyAsHintBindsModule.class); 57 | Bean bean = resolver.resolve(Bean.class); 58 | Logger expected = Logger.getLogger( 59 | BinderModule.class.getCanonicalName()); 60 | assertSame(expected.getName(), bean.logger.getName()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/container/Decorate.java: -------------------------------------------------------------------------------- 1 | package test.integration.container; 2 | 3 | import se.jbee.inject.Dependency; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.UnresolvableDependency; 6 | import se.jbee.inject.UnresolvableDependency.NoMethodForDependency; 7 | 8 | import java.lang.reflect.Array; 9 | 10 | public class Decorate { 11 | 12 | private Decorate() { 13 | throw new UnsupportedOperationException("util"); 14 | } 15 | 16 | /** 17 | * Chains {@link Injector}s similar to a {@link ClassLoader} hierarchy. 18 | * 19 | * @since 8.1 20 | * @param root context acting as fallback 21 | * @param branch context that is tried first when resolving dependencies 22 | * @return An {@link Injector} with both contexts where root acts as 23 | * fallback 24 | */ 25 | public static Injector hierarchy(Injector root, Injector branch) { 26 | return new Injector() { 27 | 28 | @Override 29 | public T resolve(Dependency dep) 30 | throws UnresolvableDependency { 31 | if (dep.type().arrayDimensions() == 1) 32 | return Decorate.resolveArray(dep, root, branch); 33 | try { 34 | return branch.resolve(dep); 35 | } catch (UnresolvableDependency.ResourceResolutionFailed | NoMethodForDependency e) { 36 | return root.resolve(dep); 37 | } 38 | } 39 | 40 | }; 41 | } 42 | 43 | @SuppressWarnings("unchecked") 44 | static T resolveArray(Dependency dep, Injector root, 45 | Injector branch) { 46 | T branchInstance = null; 47 | try { 48 | branchInstance = branch.resolve(dep); 49 | } catch (UnresolvableDependency e) { 50 | return root.resolve(dep); 51 | } 52 | T rootInstance = null; 53 | try { 54 | rootInstance = root.resolve(dep); 55 | } catch (UnresolvableDependency e) { 56 | return branchInstance; 57 | } 58 | int rootLength = Array.getLength(rootInstance); 59 | int branchLength = Array.getLength(branchInstance); 60 | Object arr = Array.newInstance(dep.type().baseType().rawType, 61 | rootLength + branchLength); 62 | System.arraycopy(rootInstance, 0, arr, 0, rootLength); 63 | System.arraycopy(branchInstance, 0, arr, rootLength, branchLength); 64 | return (T) arr; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/contract/TestRoundRobinComputeEvents.java: -------------------------------------------------------------------------------- 1 | package test.integration.contract; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | import se.jbee.inject.Injector; 6 | import se.jbee.inject.bootstrap.Bootstrap; 7 | import se.jbee.inject.contract.ContractModule; 8 | 9 | import static org.junit.jupiter.api.Assertions.*; 10 | import static se.jbee.inject.Name.named; 11 | 12 | class TestRoundRobinComputeEvents { 13 | 14 | private interface Handler { 15 | 16 | int compute(int x); 17 | } 18 | 19 | private static final class Service implements Handler { 20 | 21 | private final int inc; 22 | 23 | public Service(int inc) { 24 | this.inc = inc; 25 | } 26 | 27 | @Override 28 | public int compute(int x) { 29 | return x + inc; 30 | } 31 | 32 | } 33 | 34 | private static final class TestRoundRobinComputeEventsModule 35 | extends ContractModule { 36 | 37 | @Override 38 | protected void declare() { 39 | handle(Handler.class); 40 | bind(named("a"), Service.class).to(new Service(5)); 41 | bind(named("b"), Service.class).to(new Service(7)); 42 | bind(named("c"), Service.class).to(new Service(3)); 43 | } 44 | } 45 | 46 | private final Injector injector = Bootstrap.injector( 47 | TestRoundRobinComputeEventsModule.class); 48 | 49 | @Test 50 | @Disabled("TODO #80 // TestRoundRobinComputeEvents.computationUsesAllAvailableServices()") 51 | void computationUsesAllAvailableServices() { 52 | Handler h = injector.resolve(Handler.class); 53 | Service a = injector.resolve("a", Service.class); 54 | Service b = injector.resolve("b", Service.class); 55 | Service c = injector.resolve("c", Service.class); 56 | 57 | assertNotNull(a); 58 | assertNotNull(b); 59 | assertNotNull(c); 60 | assertNotSame(a, b); 61 | assertNotSame(b, c); 62 | assertNotSame(a, c); 63 | 64 | int sum = 0; 65 | sum += h.compute(1); 66 | sum += h.compute(1); 67 | sum += h.compute(1); 68 | assertEquals(18, sum); // 5+1 + 7+1 + 3+1 = 18 (no guarantee for order) 69 | 70 | // next round 71 | sum += h.compute(3); 72 | sum += h.compute(3); 73 | sum += h.compute(3); // 5+3 + 7+3 + 3+3 = 24 (no guarantee for order) 74 | assertEquals(42, sum); // 18 + 24 = 42 75 | } 76 | } 77 | --------------------------------------------------------------------------------

33 | * The implementation could also do an entirely different thing as long as 34 | * the result is equivalent to calling the provided {@link Method} with the 35 | * provided arguments. 36 | * 37 | * @see Method#invoke(Object, Object...) 38 | */ 39 | Object call(Method target, Object instance, Object[] args) throws Exception; 40 | } 41 | -------------------------------------------------------------------------------- /se.jbee.lang/main/java/se/jbee/lang/Qualifying.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.lang; 7 | 8 | /** 9 | * When determining what resource is used to inject a dependency 10 | * everything is sorted starting with the most {@link Qualifying}. The most 11 | * qualified match is injected. 12 | * 13 | * @author Jan Bernitt (jan@jbee.se) 14 | * 15 | * @param The type of objects that are compared 16 | */ 17 | @FunctionalInterface 18 | public interface Qualifying> { 19 | 20 | /** 21 | * @param other instance to compare with, not null 22 | * @return Whether or not this object or more qualified than the given one. 23 | * 24 | * Equal objects are do not define one of them as more qualified! 25 | * Also objects that have no common context or relationship don't 26 | * define one of them as more qualified. 27 | * 28 | * For example two {@link Type}s with no common super-type do not 29 | * define one of them as more qualified. 30 | */ 31 | boolean moreQualifiedThan(T other); 32 | 33 | static , B extends Qualifying> boolean compareRelated( 34 | A a1, A a2, B b1, B b2) { 35 | return a1.moreQualifiedThan(a2) // sequence in OR is very important!!! 36 | || !a2.moreQualifiedThan(a1) && b1.moreQualifiedThan(b2); 37 | } 38 | 39 | static > int compare(A one, A other) { 40 | return one.moreQualifiedThan(other) 41 | ? -1 42 | : other.moreQualifiedThan(one) ? 1 : 0; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/New.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.config; 2 | 3 | import java.lang.reflect.Constructor; 4 | 5 | /** 6 | * {@link New} is the abstraction of the {@link Constructor#newInstance(Object...)} 7 | * call so that the actual use of reflection can be customised. 8 | *

13 | * This basic implementation does not have any particular guarantees of fairness 14 | * in the presence of a changing list due to dynamic connects and disconnects. 15 | */ 16 | public final class RoundRobinDispatch implements 17 | ActionDispatch { 18 | 19 | private final Injector context; 20 | private final ActionExecutor executor; 21 | private final AtomicInteger callCount = new AtomicInteger(); 22 | 23 | public RoundRobinDispatch(Injector context, ActionExecutor executor) { 24 | this.context = context; 25 | this.executor = executor; 26 | } 27 | 28 | @Override 29 | public B execute(A input, List> sites) { 30 | int disconnected = 0; 31 | while (disconnected < sites.size()) { 32 | int i = callCount.getAndIncrement(); 33 | ActionSite site = sites.get(i % sites.size()); 34 | try { 35 | return executor.execute(site, site.args(context, input), input); 36 | } catch (DisconnectException ex) { 37 | disconnected++; // test the next 38 | } 39 | } 40 | throw new DisconnectException("All sites disconnected"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /se.jbee.inject.contract/main/java/se/jbee/inject/contract/PolicyProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.contract; 7 | 8 | /** 9 | * A function to derive event handling {@link EventPolicy} from the event type. 10 | * 11 | * This should allow users to use their own mechanism and logic. For example the 12 | * use of user annotations to describe isolation and such preferences that are 13 | * checked by the users implementation of this interface to convert them to a 14 | * universal description: {@link EventPolicy}. 15 | * 16 | * To substitute the default behaviour (which used {@link EventPolicy#DEFAULT}) 17 | * simply bind {@link PolicyProvider} to a custom supplier having the behaviour you 18 | * want. 19 | * 20 | * @since 8.1 21 | */ 22 | @FunctionalInterface 23 | public interface PolicyProvider { 24 | 25 | /** 26 | * Extracts the {@link EventPolicy} to use for the given event type. 27 | * 28 | * The implementation uses reflection to look at signatures and/or 29 | * annotations to derive the {@link EventPolicy} which are used by the 30 | * {@link EventProcessor} to control the processing of events of the given 31 | * type. 32 | * 33 | * @param handlerType the type of the event (the event handler interface) 34 | * @return the {@link EventPolicy} to use. 35 | */ 36 | EventPolicy reflect(Class handlerType); 37 | } 38 | -------------------------------------------------------------------------------- /test.integration/test/java/test/integration/bind/TestFeatureLiftOnlyWhenBinds.java: -------------------------------------------------------------------------------- 1 | package test.integration.bind; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import se.jbee.inject.Injector; 5 | import se.jbee.inject.Lift; 6 | import se.jbee.inject.binder.BinderModule; 7 | import se.jbee.inject.bootstrap.Bootstrap; 8 | 9 | import java.util.function.Predicate; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | /** 14 | * Minimal test that shows how {@link Lift#onlyWhen(Predicate)} can be used 15 | * to add further conditions to a {@link Lift}. 16 | */ 17 | class TestFeatureLiftOnlyWhenBinds { 18 | 19 | private static class TestFeatureLiftOnlyWhenBindsModule 20 | extends BinderModule { 21 | 22 | @Override 23 | protected void declare() { 24 | Lift lift = (target, as, context) -> target.intValue() + 1; 25 | lift(Number.class).to(lift.onlyWhen(c -> c == Integer.class)); 26 | 27 | //OBS! need to use toScoped so Lifts are applied to a constant 28 | bind(Integer.class).toScoped(2); 29 | bind(Long.class).toScoped(2L); 30 | } 31 | } 32 | 33 | private final Injector context = Bootstrap.injector( 34 | TestFeatureLiftOnlyWhenBindsModule.class); 35 | 36 | @Test 37 | void liftOnlyWhenFilterIsApplied() { 38 | assertEquals(3, context.resolve(Integer.class), 39 | "Integer did not get lifted as expected"); 40 | assertEquals(2L, context.resolve(Long.class), 41 | "Long did get lifted which must mean the filter did not work"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /se.jbee.inject.action/main/java/se/jbee/inject/action/Action.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2019, Jan Bernitt 3 | * 4 | * Licensed under the Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | package se.jbee.inject.action; 7 | 8 | import se.jbee.lang.Type; 9 | 10 | import static se.jbee.lang.Type.raw; 11 | 12 | /** 13 | * The low user level representation of an action (a operation or 14 | * micro-service). 15 | * 16 | * @see ActionExecutor 17 | * 18 | * @param The type of the input 19 | * @param The type of the output 20 | */ 21 | @FunctionalInterface 22 | public interface Action { 23 | 24 | @SuppressWarnings({ "unchecked", "rawtypes" }) 25 | static Type> actionTypeOf(Type in, Type out) { 26 | return (Type) raw(Action.class).parameterized(in, out); 27 | } 28 | 29 | static Type> actionTypeOf(Class in, Class out) { 30 | return actionTypeOf(raw(in), raw(out)); 31 | } 32 | 33 | /** 34 | * Runs the action to compute the output from the input. This typically does 35 | * not change any inner state but it might in rare cases be part of an 36 | * action to do such a thing. 37 | * 38 | * @param input might be null for {@link Void} arguments or when argument 39 | * was resolved to null 40 | * @return might be null 41 | * @throws ActionExecutionFailed wraps all {@link Exception}s thrown by the 42 | * underlying method. 43 | */ 44 | B run(A input) throws ActionExecutionFailed; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/binder/spi/ConnectorBinder.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.binder.spi; 2 | 3 | import se.jbee.inject.Env; 4 | import se.jbee.inject.Name; 5 | import se.jbee.inject.config.Connector; 6 | import se.jbee.inject.config.ProducesBy; 7 | 8 | import static se.jbee.inject.Name.named; 9 | 10 | @FunctionalInterface 11 | public interface ConnectorBinder { 12 | 13 | /** 14 | * Name of the {@link Connector} used for action feature. 15 | */ 16 | String ACTION_CONNECTOR = "actions"; 17 | 18 | String EVENT_CONNECTOR = "on"; 19 | 20 | /** 21 | * Name of the {@link Connector} used for the scheduler feature. 22 | */ 23 | String SCHEDULER_CONNECTOR = "scheduler"; 24 | 25 | /** 26 | * The qualifier {@link Name} used in the {@link Env} for a special {@link 27 | * ProducesBy} used in connection with {@link Connector}. If no special 28 | * {@link ProducesBy} is set the default named one is used. 29 | */ 30 | String CONNECT_QUALIFIER = "connect"; 31 | 32 | void to(Name connectorName); 33 | 34 | default void to(String connectorName) { 35 | to(named(connectorName)); 36 | } 37 | 38 | default void to(Class connectorName) { 39 | to(named(connectorName)); 40 | } 41 | 42 | default void asAction() { 43 | to(ACTION_CONNECTOR); 44 | } 45 | 46 | default void asEvent() { 47 | to(EVENT_CONNECTOR); 48 | } 49 | 50 | default void asScheduled(String type) { 51 | asScheduled(named(type)); 52 | } 53 | default void asScheduled(Name type) { 54 | to(type.in(SCHEDULER_CONNECTOR)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /se.jbee.inject.event/main/java/se/jbee/inject/disk/DiskScopeModule.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.disk; 2 | 3 | import se.jbee.inject.*; 4 | import se.jbee.inject.binder.BinderModule; 5 | import se.jbee.inject.binder.Installs; 6 | import se.jbee.inject.schedule.SchedulerModule; 7 | import se.jbee.inject.scope.TypeDependentScope; 8 | 9 | import java.io.File; 10 | 11 | import static se.jbee.lang.Cast.consumerTypeOf; 12 | 13 | @Installs(bundles = SchedulerModule.class) 14 | public final class DiskScopeModule extends BinderModule { 15 | 16 | @Override 17 | protected void declare() { 18 | bindLifeCycle(ScopeLifeCycle.disk); 19 | bindScope(Name.ANY.in("disk")) // 20 | .toSupplier(DiskScopeModule::createDiskScope); 21 | 22 | // usually when we save to disk we actually want to do that 23 | asDefault().bind(consumerTypeOf(DiskScope.DiskEntry.class)) // 24 | .to(DiskScope::syncToDisk); 25 | 26 | //TODO pick up "On" events 27 | } 28 | 29 | /** 30 | * By convention {@link Scope#disk(File)} create names starting with 31 | * {@code disk:} followed by the path to the folder of the scope. This can 32 | * be used to extract the directory in this {@link Supplier} to bind the 33 | * particular {@link DiskScope} for that directory. 34 | */ 35 | private static Scope createDiskScope(Dependency dep, Injector context) { 36 | return new DiskScope(new File(dep.instance.name.withoutNamespace()), 37 | TypeDependentScope::instanceSignature, context.resolve( 38 | consumerTypeOf(DiskScope.DiskEntry.class))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /se.jbee.inject.bind/main/java/se/jbee/inject/config/Get.java: -------------------------------------------------------------------------------- 1 | package se.jbee.inject.config; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | /** 6 | * {@link Get} is the abstraction of {@link Field#get(Object)} 7 | * call so that the actual use of reflection can be customised. 8 | *