Initialize spring cloud config client, for SDK development - spring

This question is somewhat similar to this existing question
I am still trying to navigate or trying to find right spring boot code, which i can customize. I need to develop java SDK which connects with existing config server and provides values to key. This SDK will be used in java applications, which might or might not be spring application. Same SDK will be used by QA for regression testing of config server.
So question is, if given
Config server URL
application name
active profile (no need for label, it will be default master),
Can I initialize some config client class which will give me simple methods like public String getKeyValue(final String key)
I am looking at source of classes like ConfigServicePropertySourceLocator, CompositePropertySource, ConfigClientAutoConfiguration, ConfigServiceBootstrapConfiguration etc.
Do I need to build Environment object manually? If yes, how?

I have some success. Posting a possible answer for others to further fine tune it.
#SpringBootApplication
public class ConfigSDKApp {
#Autowired
public SomeSpringBean someBean = null;
private static ConfigSDKApp INSTANCE = null;
public synchronized static ConfigSDKApp getInstance(String[] args) {
if (null != INSTANCE) {
return INSTANCE;
}
SpringApplication sprApp = new SpringApplication(ConfigSDKApp.class);
sprApp.setWebEnvironment(false);
ConfigurableApplicationContext appContext = sprApp.run(args);
ConfigSDKApp app = appContext.getBean(ConfigSDKApp.class);//new ConfigSDKApp();
INSTANCE = app;
return INSTANCE;
}
}
It's kind of singleton class (but public constructor). Hence code smell.
Also, what if this SDK is running with-in springboot client. ApplicationContext & environment is already initialized.

Related

Enable by default an Actuator Endpoint in Spring Boot

I developed a small library that adds a custom endpoint for the actuator and I like to expose it by default. Spring Boot 2.7.4 only exposes by default health.
At the moment, what I am doing is registering an EnvironmentPostProcessor to add a property to include health,jwks at the last PropertySource in the environment. But it seems a little bit fragile. There are other libraries that have to export other endpoints by default (metrics, prometheus...)
This is what I am doing at the moment:
public class PoCEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_NAME = "management.endpoints.web.exposure.include";
#Override
public void postProcessEnvironment(
ConfigurableEnvironment environment,
SpringApplication application
) {
var propertySources = environment.getPropertySources();
propertySources.stream()
.filter(it -> it.containsProperty(PROPERTY_NAME))
.findFirst().ifPresentOrElse(source -> {
var property = source.getProperty(PROPERTY_NAME);
var pocSource = new MapPropertySource(PROPERTY_NAME, Map.of(PROPERTY_NAME, property + ",jwks"));
// Add the new property with more priority
propertySources.addBefore(source.getName(), pocSource);
}, () -> {
var pocSource = new MapPropertySource(PROPERTY_NAME, Map.of(PROPERTY_NAME, "health,jwks"));
propertySources.addLast(pocSource);
});
}
}
Is there any way to expose by default that allow me to add several endpoints in different libraries without playing to much with the property sources?
It’s not exactly clear to me if you’re asking how the client apps that use your library would enable specific endpoints, or if you are writing more than one library and want to expose different endpoints. I’ll answer both.
management.endpoints.web.exposure.include=comma-separated-endpoints would enable the listed endpoints without your library having to do anything. Your client apps can set this property in application.yml.
If you want to set this property by default in your library, one of the easiest ways is to put it in a property file, and load it as a #PropertySource on a #Configuration bean. I’m assuming your library is a starter and the #Configuration bean is auto-configured. If you don’t know how to create a starter, refer to this article.

How to get the value of Azure App Configuration Feature Flag with specific label from Spring boot?

I started using Azure App Configuration service and Feature Flags functionality in my project. I followed this documentation and was able to create a Spring boot project. I also created a FeatureFlagService class and autowired the FeatureManager class in it as shown below :
#Service
public class FeatureFlagService {
private FeatureManager featureManager;
public FeatureFlagService(FeatureManager featureManager) {
this.featureManager = featureManager;
}
public boolean isFeatureEnabled() {
return featureManager.isEnabledAsync("scopeid/MyFeature").block();
}
}
With this I get the value of the feature flag 'MyFeature' but with no label.
I have the same feature defined with different labels in Azure App Configuration as shown below.
I need to fetch the feature flag with specific label. How can I achieve it at runtime?
I don't see a way to do it using the FeatureManager class.
They only way to load from a label is by using spring.cloud.azure.appconfiguration.stores[0].feature-flags.label-filter, the Feature Management Library itself has no concept of a label.

Configuring Tomee to allow WebSphere JNDI Lookups

We are currently developing an application intended for deployment on a WebSphere server. The application should use an in-house Service Provider, that provides access to services implemented as remote EJBs. The Service Provider bean has some hard-coded jndi-names to use.
Now during development we are using Tomee and in general all is working nicely. All except one thing:
The ServiceProvider does a jndi-lookup of "cell/persistent/configService". Now I tried to create a mock ear that contains mock EJBs for these services. I am able to deploy them, and I am able to access them from the application using jndi-names like: "java:global/framework-mock-ear-1.0.0-SNAPSHOT/framework-mock-impl/ConfigServiceMock" but it seems to be impossible to access them using a jndi lookup of: "cell/persistent/configService" ... now I added an openejb-jar.xml file to my mock implementation containing:
<openejb-jar>
<ejb-deployment ejb-name="ConfigServiceMock">
<jndi name="cell/persistent/configService"
interface="de.thecompany.common.services.config.ConfigService"/>
</ejb-deployment>
</openejb-jar>
And I can see during startup, that the bean seems to be registered correctly under that name:
INFORMATION: Jndi(name=cell/persistent/configService) --> Ejb(deployment-id=ConfigServiceMock)
But I have now idea how to make the other ear be able to access this bean using that name.
The Service Provider part is given and we are not able to change this at all, so please don't suggest to change the hard-coded jndi names. We surely would like to do so, but are not able to change anything.
Ok ... to I wasted quite some time on this. Until I finally came up with a solution. Instead of configuring Tomee and OpenEJB to find my beans, I hijacked the InitialContext and rewrote my queries.
package de.mycompany.mock.tomee;
import org.apache.naming.java.javaURLContextFactory;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Hashtable;
public class MycompanyNamingContextFactory extends javaURLContextFactory {
private static Context initialContext;
#Override
public Context getInitialContext(Hashtable environment) throws NamingException {
if(initialContext == null) {
Hashtable childEnv = (Hashtable) environment.clone();
childEnv.put("java.naming.factory.initial", "org.apache.naming.java.javaURLContextFactory");
initialContext = new MycompanyInitialContext(childEnv);
}
return initialContext;
}
}
By setting the system property
java.naming.factory.initial=de.mycompany.mock.tomee.MycompanyNamingContextFactory
I was able to inject my MycompanyInitialContext context implementation:
package de.mycompany.mock.tomee;
import org.apache.openejb.core.ivm.naming.IvmContext;
import org.apache.openejb.core.ivm.naming.NameNode;
import javax.naming.NamingException;
import java.util.Hashtable;
public class MycomanyInitialContext extends IvmContext {
public MycomanyInitialContext(Hashtable<String, Object> environment) throws NamingException {
super(environment);
}
#Override
public Object lookup(String compositName) throws NamingException {
if("cell/persistent/configService".equals(compositName)) {
return super.lookup("java:global/mycompany-mock-ear-1.0.0-SNAPSHOT/mycompany-mock-impl/ConfigServiceMock");
}
if("cell/persistent/authorizationService".equals(compositName)) {
Object obj = super.lookup("java:global/mycompany-mock-ear-1.0.0-SNAPSHOT/mycompany-mock-impl/AuthServiceMock");
return obj;
}
return super.lookup(compositName);
}
}
I know this is not pretty and if anyone has an idea how do make this easier and prettier, I'm all ears and this solution seems to work. As it's only intended on simulating production services during development, this hack doesn't induce any nightmares for me. Just thought I'd post it, just in case someone else stumbles over something similar.
I know this answer is coming a few years after the question, but a simpler solution would be to simply set the system property as follows (say in catalina.properties):
java.naming.initial.factory=org.apache.openejb.core.OpenEJBInitialContextFactory
This allows you to lookup the ejb by the name you set, and the one that shows in tomee logs during startup, eg your 'cell/persistent/configService' from
INFORMATION: Jndi(name=cell/persistent/configService) --> Ejb(deployment-id=ConfigServiceMock)
With the system property set you can lookup the ejb the way you would want
final Context ctx = new InitialContext();
ctx.lookup("cell/persistent/configService")
The OpenEJBInitialContextFactory allows access to local EJBs as well as container resources.
If you didn't want to set the system property (as it would affect all applications in the tomee) you could still use the factory setting it the 'standard' way:
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.core.OpenEJBInitialContextFactory");
final Context ctx = new InitialContext(properties);
ctx.lookup("cell/persistent/configService");
And of course you could still look them up using the global "java:global/" as well with that factory.

Using Autofac in the class library

i'm trying to solve a problem with the Autofac IoC container.
My web application has the following parts:
Web application
Core (shared) library.
Modules
Each module has a reference to the core library (that includes the code below) and each module must be able to register their own types. Therefore i need to have one shared container between application and modules.
I have the following container's code:
public static class DependencyContainer
{
public static IContainer Container { get; private set; }
public static ContainerBuilder Builder { get; private set; }
public static void RegisterType<TFrom, TTo>() where TTo : TFrom
{
Builder.RegisterType<TTo>().As<TFrom>();
}
public static T ResolveType<T>()
{
return Container.Resolve<T>();
}
public static void InitContainer()
{
Builder = new ContainerBuilder();
Container = Builder.Build();
}
}
This container is located in the main (core) library. When application starts it calls InitContainer() from global.asax. After this i'm trying to register a new type from my application modules using RegisterType() method.
But after this i can not resolve any types. It just throws an exception that this type wasn't registered.
The main idea of this code is that i'm going to be able to resolve any type from any module and web application using only one shared container.
Can somebody help me with this problem? Is there a more elegant architectural solution for using one shared IoC container between multiple libraries?
Thanks in advance!
Your design leads to the Service Locator anti-pattern. Prevent from defining a container in the core library and letting everybody reference it. Use dependency injection and configure the container in the Composition Root, your start-up path of the application (in your case the composition root will be part of your Web Application).
You can take advantage of the Autofac Module feature, and in the composition root, you can register this module.
builder.RegisterModule(new CarTransportModule());
But you can define your module registration classes as static as well, which is even simpler:
MyApp.SomeModule.SomeModuleBootstrapper.Register(builder);
Problem solved - we need to call container.Build() only AFTER registration of all our dependencies...

Automatic configuration reinitialization in Spring

In Log4j, there is a feature wherein the system can be initialized to do a configure and watch with an interval. This allows for the log4j system to reload its properties whenever the property file is changed. Does the spring framework have such a Configuration Observer facility wherein the Configuration is reloaded when it changed. The Configuration that needs reloading is not the Springs's applicationContext.xml but various other configuration files that are initialized using the Spring initialization beans.
I found a utility that does something similar to Log4J here. It's basically an extension to PropertyPlaceholderConfigurer that reloads properties when they change.
AFAIK Spring does not provide such a utility. However there is a 3rd party tool, JRebel that enables you to update an entire web application (including the Spring configuration) without requiring a server restart.
A free trial is available, and the purchase price is fairly inexpensive.
I would be extra cautious with reloading spring application context.
What do you expect to happen with singleton beans? If an object has a reference to singleton bean, should it be updated?
I develop using JRebel and I would be very wary of expecting it to refresh your configuration. Works fine with Java, not with Spring though.
If you would like to add context, I have done that in the following way :
public class ApplicationContextUtil
{
static String[] configFiles = {"applicationContextParent.xml"};
private static ApplicationContext context = null;
static
{
context = new ClassPathXmlApplicationContext ( configFiles );
}
public static void addContext( String[] newConfigFiles )
{
// add the new context to the previous context
ApplicationContext newContext = new ClassPathXmlApplicationContext ( newConfigFiles, context );
context = newContext;
}
public static ApplicationContext getApplicationContext ()
{
// return the context
return context;
}
}
This is your context provider class. For details, you can look at my blog

Resources