Accessing config instance from a Lagom service descriptor - scala

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!

Related

Configure logging programmatically in Scala/Play

The Play framework requires (by default) that you configure logging through a logback.xml file. I'd like to build my log appenders through code so I can fetch parameters at runtime (e.g. the graylog destination for the logs is fetched from the deployment environment, rather than baking it in statically through an XML file).
This sort of thing is fairly easy to achieve in Java (by overriding logging factories and the like), I wondered if the same were possible in Play.
Yes, you can configure logback programmatically, see: https://akhikhl.wordpress.com/2013/07/11/programmatic-configuration-of-slf4jlogback/
But I wouldn't recommend it. For starters it's a verbose API that isn't pleasant to work with. Beyond that, it generally nice for configuration to be declarative (even if it is in XML in this case).
For your usecase, Logback's XML does support variables which can come from System properties or Environment variables: https://logback.qos.ch/manual/configuration.html#definingProps
However, you probably want a different config across environments (no greylog locally). I think many projects do that by specifying the logback XML location as a system property at startup: https://logback.qos.ch/manual/configuration.html#configFileProperty
Alternatively, I suspect greylog has some method of watching a file to pickup your logging. That's what we do for picking up logs in Splunk in my team. We don't want to make a change to our code when someone reconfigures Splunk/Greylog.
The solution I used in the end was to use a logback contextlistener to populate the context with the parameters pulled from the environment. The listener can be added as follows to the logback.xml:
<contextListener class="LoggerStartup"/>
The LoggerStartup can then populate the context, which I achieved through AWS SSM (see the simplified code below).
class LoggerStartup extends ContextAwareBase with LoggerContextListener with LifeCycle {
override def start() = {
val context = getContext()
val graylogUrl = ... // Go get value from remote store
context.putProperty("GRAYLOG_URL", graylogUrl)
}
}
And then referenced this context variable in the logback file:
<appender name="GELF UDP APPENDER" class="me.moocar.logbackgelf.GelfUDPAppender">
<remoteHost>${GRAYLOG_URL}</remoteHost>
...
</appender>

Dynamically update Eureka instance metadata

When Spring Cloud Eureka instance starts I can define some instance metadata statically (in eureka.instance.metadataMap.* in my application.yml) or dynamically (using EurekaInstanceConfigBean for example). But once instance is registered, this metadata no longer updates in Eureka after I update the config bean.
Is there a way to define some metadata that will dynamically update in Eureka? So Eureka will work kind of like a key-value storage for each instance.
If you want to update any metadata from eureka client for itself, just use com.netflix.appinfo.ApplicationInfoManagerobject and call registerAppMetadata(Map<String, String>).
If so, this info will be updated in Eureka Server usually soon or at least in 30sec.You can use DI to get the instance of ApplicationInfoManger.
If you want to update metadata for other service instance, just invoke REST API like below to eureka server.
PUT /eureka/apps/appID/instanceID/metadata?key=value

What is the criteria to choose between IocService and RegistryBuilder in Fantom afIoc

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()

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.

Spring Boot Starter Data Rest change URL of repository from the root URI

Following the spring.io example here: http://spring.io/guides/gs/accessing-data-rest/ for exposing a repository as a rest web service works just fine, but I cannot see how to change the URL of the exposed service. The API documentation is a little vague as to what the annotation parameters mean, perhaps some prior knowledge is assumed.
What I want - A HATEOAS service accessed at http://localhost:8080/api/people for a People repository. I want to achieve this URL using annotations only, not messing with the context root or similar. I tried the following repository annotations:
#RepositoryRestResource(collectionResourceRel = "api/people", path = "people")
#RepositoryRestResource(collectionResourceRel = "people", path = "api/people")
#RepositoryRestResource(collectionResourceRel = "api/people", path = "api/people")
None of these work.
I know I have probably missed the obvious, much appreciate anyone who can point it out.
As of Spring Boot 1.2 you are able to set this property:
spring.data.rest.baseUri=api
Alternatively:
spring.data.rest.base-uri=api
(Spring Boot uses a relaxed binding system)
NOTE: I have found that if you have extended RepositoryRestMvcConfiguration with custom configuration, the property does not take effect. For more information see:
https://github.com/spring-projects/spring-boot/issues/2392
Once the next version of Spring Boot is released (after 1.2.1), the solution will be to extend RepositoryRestMvcBootConfiguration instead.
As of Spring Boot 1.4.3 the code should be :
spring.data.rest.base-path:api
(I think baseUri is deprecated since 1.2.3)
Although I couldn't change the base path of the REST services using the annotation #RepositoryRestResource combined with a CrudRepository, I managed to do it using a JpaRepository and a custom controller with the annotation #RequestMapping.
The repository could be something like:
#Repository
interface PersonRepository : JpaRepository<Person, Long>
And the controller:
#RestController
#RequestMapping("/api/people")
class PersonRestController(private val personRepository: PersonRepository) {
...
On the other hand, you can change the base path of all your REST services modifying it in the application.properties file of your project. Add the lines:
# DATA REST (RepositoryRestConfiguration)
spring.data.rest.base-path = api
Change api with the path you wish you use in your URLs. The first line is a comment and, as so, it's not mandatory, but is useful to mark the nature of the configuration value for future references.
You can find all the common application properties of Spring Boot 2.0.1 in the Appendix A of the documentation.