What is the criteria to choose between IocService and RegistryBuilder in Fantom afIoc - inversion-of-control

The documentation of Alien Factory's IoC framework for Fantom says:
You can use IocService to start IoC as a Fantom service:
IocService([MyModule#]).start
...
reg := ((IocService) Service.find(IocService#)).registry
service := reg.dependencyByType(MyService#)
...
Service.find(IocService#).uninstall
Or use RegistryBuilder to manage the Registry instance manually;
reg := RegistryBuilder().addModule(MyModule#).build.startup
...
service := reg.dependencyByType(MyService#)
...
reg.shutdown
But what is the criteria to decide the appropriate way of initialising the registry on a specific scenario?

The short answer - use RegistryBuilder.
The long answer prompted me to update the documentation... it's ongoing, but here's the current revision:
Building the Registry
Use RegistryBuilder to manually manage an IoC Registry instance. You would typically do this when running tests.
registry := RegistryBuilder().addModule(MyModule#).build().startup()
...
service := registry.dependencyByType(MyService#)
...
registry.shutdown()
Ensure modules are added from other IoC libraries the code uses. Example, if using the IocEnv library then add IocEnvModule:
registry := RegistryBuilder().addModule(MyModule#).addModule(IocEnvModule#).build().startup()
It's standard that IoC library modules are named after the library, but with a Module suffix.
The IocService
If your code runs in an IoC container, such as BedSheet, then the container manages the Registry instance for you.
If running unit tests then typically you would create your own Registry instance and hold it as a variable / field.
An alternative is to create a Fantom Service to hold the registry. This is useful in situations where static access to the Registry, such as fwt applications where you have very little control over how your classes are created.
IocService is a helper class that extends 'Service' and contains convenience methods for creating and accessing the registry.
For example, to create and start a Fantom IoC Service:
IocService([MyModule#]).start()
Then from anywhere in your code, it may be accessed with:
iocService := (IocService) Service.find(IocService#)
...
myService := iocService.serviceById(MyService#.qname)
Uninstall IocService like any other:
Service.find(IocService#).uninstall()

Related

Using environment variables to configure Docker deployment of Lagom Scala application

We're developing several Lagom-based Scala micro-services. They are configured using variable replacement in application.conf, eg.
mysql = {
url = "jdbc:mysql://"${?ENV_MYSQL_DATABASE_URL}
During development, we set these variables as Java System Properties via a env.sbt file that calls System.setProperty("ENV_MYSQL_DATABASE_URL", url). This is working fine.
Now I want to deploy this in a container to my local Docker installation. We are using the SbtReactiveAppPlugin to build the Docker image from build.sbt and simply run sbt Docker/publishLocal. This works as expected, a Docker image is created and I can fire it up.
However, passing in environment variables using the standard docker or docker-compose mechanisms does not seem to work. While I can see that the environment variables are set correctly inside the Docker container (verified using env on a bash and also by doing log.debug("ENV_MYSQL_DATABASE_URL via env: " + sys.env("ENV_MYSQL_DATABASE_URL")) inside the service), they are not used by the application.conf and not available in the configuration system. The values are empty/unset (verified through configuration.getString("ENV_MYSQL_DATABASE_URL").toString() and the exceptions thrown by the mysql system and other systems).
The only way I've gotten it to work was by fudging this into the JAVA_OPTS via JAVA_OPTS=-D ENV_MYSQL_DATABASE_URL=..... However, this seems like a hack, and doesn't appear to scale very well with dozens of environment parameters.
Am I missing something, is there a way to easily use the environment variables inside the Lagom application and application.conf?
Thanks!
I've used Lightbend config to configure Lagom services via environment variables in docker containers for many years, so know that it can be done and has been pretty straightforward in my experience.
With that in mind, when you say that they're not used by application.conf, do you mean that they're unset? Note that unless you're passing a very specific option as a Java property, configuration.getString("ENV_MYSQL_DATABASE_URL") will not read from an environment variable, so checking that will not tell you anything about whether mysql.url is affected by the environment variable. configuration.getString("mysql.url") will give you a better idea of what's going on.
I suspect that in fact your Docker image is being built with the dev-mode properties hardcoded in, and since Java system properties take precedence over everything else, they're shadowing the environment variable.
You may find it useful to structure your application.conf along these lines:
mysql_database_url = "..." # Some reasonable default default for dev-mode
mysql_database_url = ${?ENV_MYSQL_DATABASE_URL}
mysql {
url = "jdbc://"${mysql_database_url}
}
In this case, you have a reasonable default for a developer (probably including in the docs some instructions for running MySQL in a way compatible with that configuration). The default can then be overridden via setting a Java property (e.g. JAVA_OPTS=-Dmysql_database_url) or by setting the ENV_MYSQL_DATABASE_URL environment variable.
While I agree with the answer provided by Levi Ramsey, I would suggest you to use typesafe's config to load the your config

Accessing config instance from a Lagom service descriptor

Is there a recommended way for injecting / accessing the application Typesafe config instance from within a Lagom service interface (ie, the trait, not Impl)?
The use case I am thinking of is for the creation of a request header filter that depends on a configurable value (like, an env specific secret) and injecting it via a constructor argument.
final override def descriptor: Descriptor = {
import Service._
named("some-service")
.withCalls(
pathCall("/health", healthCheck),
)
.withHeaderFilter(new CustomerHeaderFilter(config))
}
Unfortunately, from within the descriptor function, there is no readily exposed reference to the config. I have tried including as an abstract field on the service but this seems to cause Lagom to bomb out and complain that the field does not generate a service.
Is there a recommended way to do this or do I essentially have to call ConfigFactory.load()?
Currently using Lagom 1.4.5 + Scala - thanks!

Windsor Ioc Auto Register Classes namespace collision for duplicate type across assemblies

I would like suggestions on if this usage is possible with Windsor and how to achieve. The details (simplified to illustrate the issue) are as follows.
AssemblyA namespace com.foo
IRepository<T>
AssemblyB namespace com.foo
Repository<Data> extends IRepository<Data>
Service -> Resolves Repository<Data> via Windsor
AssemblyC namespace com.foo
Repository<Data> extends IRepository<Data>
Service -> Resolves Repository<Data> via Windsor
AssemblyB references AssemblyA
AssemblyC references AssemblyA
Services are deployed in WCF as follows:
<add relativeAddress="v1/Service.svc" service="com.foo.Service, AssemblyB" />
<add relativeAddress="v2/Service.svc" service="com.foo.Service, AssemblyC" />
Registered via Windsor as follows:
var allTypesFromBinDir =
Classes.FromAssemblyInDirectory(
new AssemblyFilter(path));
Container.Register(allTypesFromBinDir.BasedOn(typeof(IRepository<>))
.WithService.Base()
.LifestyleTransient());
What I want is to scope registration to include the Assembly in qualifying Data for registering and resolving. What occurs is the first assembly scanned AssemblyB registers IRepository with assemblyB.Data and does not register AssemblyC IRespository with assemblyC.Data.
The following occurs or vice versa depending on order dll's scanned.
AssemblyB resolves correctly.
AssemblyC fails to resolve with "No component for supporting the service"
Container.Resolve(IRepository<Data>)
So it seems that the registration treats the two namespaces as the same from registration purpose but resolving takes assembly into account? I expect Registration to take assembly qualification into account as well.
In otherwords...
Windsor "should" I think:
Register AssemblyA.IRepository<AssemblyB.Data> with AssemblyB.Repository<AssemblyB.Data>
And
Register AssemblyA.IRepository<AssemblyC.Data> with AssemblyC.Repository<AssemblyC.Data>
Then Windsor "should" I think:
AssemblyB Service resolves AssemblyA.IRepository<AssemblyB.Data> to AssemblyB.Repository<AssemblyB.Data>
And
AssemblyC Service resolves AssemblyA.IRepository<AssemblyC.Data> to AssemblyC.Repository<AssemblyC.Data>
Instead
Windsor is:
Register AssemblyA.IRepository<AssemblyB.Data> with AssemblyB.Repository<AssemblyB.Data>
And
Does not Register AssemblyA.IRepository<AssemblyC.Data> with AssemblyC.Repository<AssemblyC.Data> I believe it matches AssemblyA.IRepository<Data> from prior registration ignoring Assembly.
Then Windsor is:
AssemblyB Service resolves AssemblyA.IRepository<AssemblyB.Data> to AssemblyB.Repository<AssemblyB.Data>
And
AssemblyC Service does not resolve AssemblyA.IRepository<AssemblyC.Data> but throws component not found. If AssemblyC is scanned first then the exception happens in AssemblyB Service.
I have looked at posts demonstrating something close to what I want by Krzysztof here using IGenericServiceStrategy
However, this strategy seems to work only with Component registration using implemented by.
Thanks in advance.
Having the same namespaces and types in different assemblies is not a good choice. It requires additional job anyway to resolve them withing your clients - extern alias <name>.
extern alias v2Alias;
extern alias v1Alias;
And it seems that when your types in the same namespace then either only types from first assembly are registered:
container.Register(Types.FromAssemblyNamed("v1.com.foo").BasedOn(typeof(IRepository<>)).WithService.Base());
container.Register(Types.FromAssemblyNamed("v2.com.foo").BasedOn(typeof(IRepository<>)).WithService.Base());
or exception 'There is already a component with that name. Did you want to modify the existing component instead? If not, make sure you specify a unique name.' is thrown:
container.Register(Component.For<IRepository<v1Alias::com.foo.Data>>().ImplementedBy<v1Alias::com.foo.Repository>());
container.Register(Component.For<IRepository<v2Alias::com.foo.Data>>().ImplementedBy<v2Alias::com.foo.Repository>());
If you put different versions of your types into different namespaces (e.g. com.foo.v1, com.foo.v2) all your types will be registered.
There'll be no difference how you register them:
container.Register(Component.For<IRepository<v1.com.foo.Data>>().ImplementedBy<v1.com.foo.Repository>());
container.Register(Component.For<IRepository<v2.com.foo.Data>>().ImplementedBy<v2.com.foo.Repository>());
or
container.Register(Types.FromAssemblyNamed("v1.com.foo").BasedOn(typeof(IRepository<>)).WithService.Base());
container.Register(Types.FromAssemblyNamed("v2.com.foo").BasedOn(typeof(IRepository<>)).WithService.Base());
PS: If this solution doesn't work for you then you can post an issue on GitHub or create a pull request with your fix.

How do I use Puppet's ralsh with resource types provided by modules?

I have installed the postgresql module from Puppetforge.
How can I query Postgresql resources using ralsh ?
None of the following works:
# ralsh postgresql::db
# ralsh puppetlabs/postgresql::db
# ralsh puppetlabs-postgresql::db
I was hoping to use this to get a list of databases (including attributes such as character sets) and user names/passwords from the current system in a form that I can paste into a puppet manifest to recreate that setup on a different machine.
In principle, any puppet client gets the current state of your system from another program called Facter. You should create a custom Fact (a module of Facter), and then included into your puppet client. Afterwards, I think you could call this custom Fact from ralsh.
More information about creating a custom Fact can be found in here.
In creating your own Fact, you should execute your SQL query and then save the result into particular variable.

GWT GIN HOW TO: injection of remote services

The gin tutorial seems to imply that to inject remote services all you need to do is annotate with #Inject.
Do you you still need to define this in a module somewhere or is the point that you can just annotate with #Inject and it will work?
Gin has automatic support for remote services, as outlined in the tutorial you mentioned:
Every time Gin is asked to inject an asynchronous remote service, it will inject an instance retrieved through calling GWT.create on its regular remote service.
Therefore, it will 'just work'.