OSGi profiles like Spring profiles - osgi

I have an OSGi service with two implementations. One for prod and one for testing.
Is there any way how to switch them in different envs via code? Something like Spring profiles

There is no direct match to profiles. What you can do is to publish both services with different service properties. On the service consumer you can then use a filter to decide which service to use.
In declarative services this filter can be changed at runtime by providing a configuration for your component. In the code you set the filter to your default like "prod" below.
#Reference(target="(profile=prod)")
MyService myService;
In the config for the component where you inject the service you can add a property to change this filter:
myService.target=(profile=dev)
Another even more common approach is to simply deploy different services for dev and prod. In your test you deploy a dummy service while in prod you deploy the real service. This approach has the advantage that it works with any DI framework in OSGi.

Related

Can you use Testcontainers to manage service dependencies, like a database, during local development?

Testcontainers can manage dockerized service dependencies, like a database, Kafka, Elasticsearch, and so on for integration testing.
Can I configure my Spring Boot application to manage these service dependencies during local development?
For example, my Spring Boot application needs a MySQL database.
I would like to integrate it with Testcontainers to provide a Docker container with MySQL not only during the tests execution, but at application startup during local development too.
Testcontainers provides an API to manage applications and services in Docker containers. It's incredibly useful for integration testing, where having a programmatically configured, isolated, repeatable environments is an essential requirement for trustworthy tests.
Because of that Testcontainers has integrations with the frameworks like Spring and Quarkus, and tes frameworks like JUnit, Spock, etc to automatically tie the lifecycle of your containerized dependencies to the lifecycle of the tests.
However, Testcontainers API is generic and doesn't have to run during the tests. For example, Quarkus has a feature called Dev Services which automatically creates a container for your database (or other service dependencies, for example Kafka, Redis, etc) when your application tries to access the database, but the configuration is not present.
You can think about it like this, if you have the data access repository classes initialized and wired, but no datasource.url in the config -- it'll spin up the database using testcontainers and configure the app to use it (just like it would happen during tests, but instead used for local development).
Spring Boot doesn't have an automated feature like that currently, there's an open issue to investigate these local development setups with Testcontainers.
If you're open to manually add a feature for your particular application, you can look at the prototype linked from that issue here: https://github.com/joshlong/testcontainers-auto-services-prototype
It's a bit more involved because it integrates with the Spring DevTools, but here are the essential parts that need to be taken care of:
Check that you need to use the database (in your application it can be a given).
Verify the configuration to use the database is absent (if the database is already configured you don't need to spin up a new one)
Create a container using Testcontainers API, either using an appropriate module or the GenericContainer with any Docker image.
Provide the configuration back to the application. For the database that would be the jdbcUrl, username, password, database name, r2dbcUrl and any other relevant properties.
You can take a look at the video with Josh Long where this concept was tried: https://www.youtube.com/watch?v=1PUshxvTbAc&t=2450s
It would also work in the production environments, but the usefulness of the ephemeral Databases, might be limited.

Including profiles via a Spring Boot starter

I'm having issues with a custom Spring Boot starter. How can a starter cause a profile to be included and pull related configuration from a config server?
Perhaps my use case is unique, because I haven't found any helpful information online. I'm working in an enterprise environment and this starter is for use by my team, so we're able to control some things (like profile names) that perhaps wouldn't make sense in the open source world.
Here is the scenario: We have a Spring Cloud Config Server running to provide configuration. Across our Spring Boot projects, we have standardized on certain profile names such as "prod" and "nonprod" to control configuration in our different environments. I am trying to create a starter to provide reusable functionality. For example purposes, let's say I'm creating a starter that provides an interface to an appliance that performs cryptographic work for us. This starter will need the IP address of the appliance and various other configuration which differs between production and non-production.
Within the config repo, I will have files such as application.yml, application-nonprod.yml, application-nonprodEncryption.yml, etc.
My goal is to have the custom encryption starter automatically include the nonprodEncryption profile when that starter is included in an application. By doing this, apps which don't need encryption do not load the encryption related properties.
Here are my experimental findings so far:
Within an application's bootstrap.yml, I can put a block such as
spring.profiles: nonprod
spring:
profiles:
include:
- nonprodEncryption
and that produces the desired result (i.e. the application-nonprodEncryption.yml file is loaded from the config server and used), but this is an undesirable solution as every app that uses my custom starter would need to include this boilerplate configuration.
When I move the above configuration to the starter's bootstrap.yml, it seems to have no effect.
When I move the above configuration to the starter's application.yml, it seems to be applied (i.e. it shows up in the The following profiles are active: list), but it is too late in the lifecycle to cause the appropriate configuration to be pulled from the config server.
Other things I've considered:
Why not just put all of the configuration into the main profile config file (e.g. application-nonprod.yml)? From a separation of concerns and maintenance standpoint, I'd like to keep configuration for individual starters isolated from each other. Also, some configuration data is more sensitive than other config data, so I don't like the idea of exposing all of the configuration to all apps, since many apps won't need some of the more sensitive configuration. Yes, they could get to it, but why load it into their memory if they don't need it?
Why not just specify the extra profiles when we launch the app? These apps will be running on a cloud platform. The platform will specify either "prod" or "nonprod" as the profile based on which tier the app is running in. I want to manage that at the platform level rather than the app level, so I want the list of profiles provided at app launch to be uniform across all apps (add adding, for example, nonprodEncryption to the list just gets me into the same situation as above - all apps would have all of the configuration, so I might as well just put it all in a single file).
We are currently using Spring Boot 1.5.10.
Any thoughts on how to achieve what I'm trying to do?
I finally found a solution (in case anyone else finds themselves in the same spot).
Step 1: Add a configuration class like this to your starter:
package com.company.bootstrap;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Profile;
#Configuration
#Profile("nonprod")
public class BootstrapNonprod {
public BootstrapNonprod(ConfigurableApplicationContext ctx) {
ctx.getEnvironment().addActiveProfile("nonprodEncryption");
}
}
This will conditionally add a profile. In this example, whenever the "nonprod" profile is active, this class will add the "nonprodEncryption" profile.
Step 2: In your starter's spring.factories file, add a line such as this:
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.company.bootstrap.BootstrapNonprod
It seems like it is just that simple.

In a spring-boot application, is it possible to change the dependency injection by external configuration?

I am building an application to send email but I want to turn off the actual send and just have it output to a log file in non-prod environments. The class that actually sends mail is a Spring injected dependency, so my thought was just to build a no-op implementation to swap out with the actual implementation.
Second requirement, I don't want to create separate builds for different environments. I want to create one and promote it through dev -> test -> prod.
This seems like a common use case for dependency injection but I don't see how to externally configure it. If I want to do it this way do I need to abandon annotation based injection and use xml instead?
You can add #Profile("PRODUCTION") on your production #Bean and #Profile("!PRODUCTION") on your non-production #Bean. Then you can specify the active profile(s) at runtime:
java -jar bootApp.jar --spring.profiles.active=PRODUCTION, ...

How to run multiple Spring Boot application sharing same context?

I want to run multiple micro-services app sharing same context so that I can run my custom security filter for multiple spring boot(micro-services) app.
Example:
User services : https://ip:port/myapp/user
Product services : https://ip:port/myapp/product
Comment services : https://ip:port/myapp/comment
And I should run a common filter(Custom Security Filter) for all micro-services.
As #luboskrnac suggests - for the security you could simply extract your common logic into a separate JAR.
Regarding your shared application context (I assume you are just referring to the shared URL space, rather than sharing any particular state across the apps etc), then yes - you should use something like Zuul - this can act as a singular interface between external and all your microservices (which, under the hood would all be running in their own unique application context namespace/port number - but Zuul can group those together and expose them with nice URLs on a consistent location).
Luckily the whole Netflix microservice stack is well supported by Spring, so its reasonably straight forward to get up and running with Zuul and Eureka (the discovery service).
I have a hello-world setup of the stack written up here (along with the code): http://automateddeveloper.blogspot.co.uk/2015/09/spring-boot-netflix-oss-adventure-into.html
Extract that filter into separate JAR that will be used by each microservice. That JAR will be separate project versioned and deployed into your artifact repository independently.
In fact in microservice environment, you will have much more "common" beans/functionality that should be shared across services. Therefore such common JAR is necessary in microservice environment.

Can I duplicate a web service for testing?

I have a REST web service exposed at http://server:8080/my-production-ws by JBoss (7). My spring (3.2) configuration has a datasource which points to a my-production-db database. So far so good.
I want to test that web service from the client side, including PUT/POST operations but I obviously don't want my tests to affect the production database.
Is there an easy way to have spring auto-magically create another web service entry point at http://server:8080/my-test-ws or maybe http://server:8080/my-production-ws/test that will have the exact same semantics as the production web service but will use a my-test-db database as a data source instead of my-production-db?
If this is not possible, what is the standard approach to integration testing in that situation?
I'd rather not duplicate every single method in my controllers.
Check the spring Profiles functionality, this should solve the problem. With it its possible to create two datasources with the same bean name in different profiles and then activate only one depending on a parameter passed to the the JVM.

Resources