SpringBoot pass local variable - java

I'm creating a Java application that depending on certain conditions/configurations it instantiates a SpringBoot application to receive some HTTP messages.
My problem is that I have a ReceiverService that needs to use some variables that are created outside of the SpringBoot application. Is there a way to pass local java variables (from inside the application, not outside like the shell or files) to SpringBoot components?
Example:
I have a Manager object that, depending on some conditions, it defines a variable param that I want to use in the SpringBoot Component ReceiverService.
public class Manager {
public Manager(bool condition) {
String param = "foo";
if (condition) {
param = "bar";
}
ReceiverApp receiver = new ReceiverApp(); // init SpringBoot app
}
}
The SpringBoot app:
#SpringBootApplication
public class ReceiverApp {
public ReceiverApp() {
SpringApplication.run(ReceiverApp.class);
}
#Component
public class ReceiverService implements InitializingBean {
final CustomObject obj1 = new CustomObject(param);
#Override
public void aFunction() throws Exception {
MyConfig config = MyConfig.build(param);
}
}

SpringApplication.run is designed to accept key=value pairs like the ones you give in command line parameters to the main method in Java.
Since you seem to have the other Spring boot application jar in your class-path, and seem to be able to just instanciate the ReceiverApp, you could just pass the parameters as strings (of the format String[]{"key1=value1", "key2=value2"})
These can be passed to SpringApplication.run, and these will automatically become Spring configuration values, which can be injected anywhere in the application.
#SpringBootApplication
public class ReceiverApp {
public ReceiverApp(String[] notReallyFromCommandLineArgs) {
SpringApplication.run(ReceiverApp.class, notReallyFromCommandLineArgs);
}
You can send the parameters like this:
String[] params = new String[]{"my.params.param1="+param1};
ReceiverApp receiver = new ReceiverApp(params);
You can inject them anywhere in that Receiver Spring application as a value.
#Value("my.params.param1")
String theParam1;
Refer:
https://stackoverflow.com/a/55890457/1364747

There are several ways to create a manager app which configures another app.
Scenario 1
A common way is to put the config in a database which is accessed directly by both apps, although this is less clean (it creates a hard dependency via the database).
Scenario 2
A cleaner solution is if the manager app owns the database, and offers a config service which is consumed (and presumably cached) by the configured app. Typically the config service would be accessed via remoting, e.g. REST, so the two apps can be deployed and run independently.
Scenario 3
If you want to keep it simple, you could reverse the setup and implement the config service in the configured app itself (sort of like the management interface in Tomcat). Then you could either add a web UI in the same app and be done with it, or build a separate standalone client which interacts with that service. In this last two scenarios, think about security because you might not want any client to be able to connect and change the config.

You can have ReceiverService inject a Supplier<String> (or define your own interface if you prefer a less generic naming) and make Manager implement it.
#Component
public class ReceiverService implements InitializingBean {
#Autowired
private Supplier<String> paramSupplier;
final CustomObject obj1 = new CustomObject(param);
#Override
public void aFunction() throws Exception {
MyConfig config = MyConfig.build(paramSupplier.get());
}
}
public class Manager implements Supplier<String> {
private final String param;
public String get() { return param; }
public Manager(bool condition) {
param = "foo";
if (condition) {
param = "bar";
}
// why is this in the Manager constructor???
ReceiverApp receiver = new ReceiverApp(); // init SpringBoot app
}
}
Or you define your own #Component to supply the parameter and have both manager and receiver inject it.

Related

Spring boot custom resolver for class variable

I'm trying to achieve something like this:
#Controller
public SomeController {
#CustomConfig("var.a")
private String varA;
#CustomConfig("var.b")
private String varB;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String get() {
return varA;
}
}
CustomConfig would be an #Interface class that accepts one value parameter. The reason why we are not using #Value is because this will not come from config file but from API (such as https://getconfig.com/get?key=var.a). So we are going to make HTTP request to inject it.
So far I've only manage to make something work if the varA and varB is inside get() method as parameter, by using below in a class that extends WebMvcConfigurerAdapter:
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
CustomConfigResolver resolver = new CustomConfigResolver();
argumentResolvers.add(resolver);
}
And inside CustomComfigResolver.resolveArgument() we would do the HTTP query, but that's not really what we wanted, we need it to be injected as class variable.
Does anyone have experience in resolving it at class variable level?
Thank you
This could work if you use #Value instead of your own custom annotation. This uses the built in environment:
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
public class TcpIpPropertySourceConfig implements InitializingBean {
#Autowired
private ConfigurableEnvironment env;
#Autowired
private RestTemplate rest;
public void afterPropertiesSet() {
// Call your api using Resttemplate
RemoteProperties props = //Rest Call here;
// Add your source to the environment.
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(new PropertiesPropertySource("customSourceName", props)
}
}
What you are trying to achieve is difficult when you start to consider "unhappy" scenarios. Server down / not reachable. You need to account for all of that in the method above.
I would highly recommend to instead use Spring Cloud Config. Great guide on that is here: https://www.baeldung.com/spring-cloud-configuration
This provides:
- Reloading of your #Value() properties, so no custom annotation needed.
- A more stable server and great Spring integration out of the box.
Best of all, it is easy to apply Retries and Backoffs if the configuration server goes down (see https://stackoverflow.com/a/44203216/2082699). This will make sure your app doesn't just crash when the server is not available.

Dynamic target for declarative service in OSGI

Given a consumer which uses a service, how can this consumer select a specific provider dynamically using declarative service ?
Example
Service.java
public interface Service {
public void do();
}
Provider1.java
public class Provider1 implements Service {
#Override
public void do(){
//a way
}
}
Provider2.java
public class Provider2 implements Service {
#Override
public void do(){
//another way
}
}
Consumer.java
public class Consumer {
private Service myService;
protected void bindService(Service s){ // Actually it's Provider1
myService = s;
}
protected void unbindService(Service s){
myService = null;
}
public void useThisKindOfService(String s){
// Do something crazy
}
}
So, what I would like it's instead of "Do something crazy", to find a way to reconfigure the consumer in order to release Provider1 and ask for Provider2.
Is it possible ?
Update related to "Duplicate Question"
OSGI/Felix Declarative services: How to filter the services to be bound
In my context I cannot use the declarative target because the value of the target has to be know at build time, in my case the target could be defined by a user at runtime.
Components of Declarative Services can be configured via ConfigurationAdmin. By doing that, the configuration of the component can be changed at runtime.
You can also change the configuration of myService.target via ConfigurationAdmin at runtime. If you do that, another reference will be bound to your component.
If the policy of the reference of your component is dynamic, the new reference will be bound without reactivating your component.
For more information, see the Declarative Services chapter of the OSGi Compendium specification.

Testing in OSGIContext

could you suggest any working tip, how to change order of services in OSGI test environment without modifying of tested classes and generated descriptors?
I am testing a custom class, initialized in test OSGI environment, org.apache.sling.testing.mock.osgi.junit.OsgiContext, with a List of multiple service instances implementing the same interface. Order of service instances is specified statically with SERVICE_RANKING property annotation and generated xml descriptors in OSGI-INF folder.
I would like to simulate switch of the SERVICE_RANKING and the oder of services in local test environment in the way, how service order could be switched in the real OSGI container.
Service class example:
#Component
#Service(MyService.class)
public class PlainService implements MyService {
#Property(label = "Service Ranking", intValue = PlainService.DEFAULT_RANKING,
description = "set lower value to get higher priority of service",
propertyPrivate = false)
static final String PROPERTY_RANKING = Constants.SERVICE_RANKING;
static final int DEFAULT_RANKING = 999;
...
}
Service container class example:
#Component
#Service(MyService.class)
public class PlainServiceContainer implements MyServiceContainer {
#Reference(name = "myService", referenceInterface = MyService.class,
cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
private final RankedServices<MyService> myService = new RankedServices<>();
...
void bindMyService(MyService service, Map<String, Object> props) {
...
}
void unbindMyService(MyService service, Map<String, Object> props) {
...
}
}
I expect there could be a property definition, which have to change SERVICE_RANKING at the moment of service initialization:
OsgiContext context = new OsgiContext();
context.registerInjectActivateService(new PlainService(), ImmutableMap.of(Constants.SERVICE_RANKING, 989));
The actual example with ranking 989 does not work and service is still initialized with ranking 999, but there should be working case, which changes it.

Is it possible to use the Multibinder within Guice to construct dynamic sets based on application configuration?

I have an application which relies on Properties configuration to determine whether to mix in various components or not.
For example, the configuration has boolean flags like "componentX.enabled" etc which determine whether these components should be active or not.
Currently I am using these flags in my provider methods like so:
#Provides
#Singleton
#Nullable
public ComponentX provideComponentX(Properties props) {
if (props.isComponentXEnabled()) {
return new ComponentX();
} else {
return null;
}
}
#Provides
#Singleton
public Set<Component> provideComponentSet(
#Nullable ComponentX compX,
ComponentY compY,
ComponentZ compZ
) {
Set<Component> comps = new HashSet<>();
if (compX != null) {
comps.add(compX);
}
comps.add(compY);
comps.add(compZ);
return comps;
}
This approach seems a little clunky (it relies on possible injecting null)- but is there a better way?
The only other way I can think of doing it is by using a parent injector to obtain the application Properties into my module, and then using the set Multibinder.
Then use the create child injector with the new module to complete the bootstrap process.
public class Module extends AbstractModule {
Properties props;
public Module(Properties props) {
this.props = props;
}
public void configure() {
Multibinder<Component> compBinder = Multibinder.newSetBinder(binder(), Component.class);
if (props.isComponentXEnabled()) {
compBinder.addBinding().to(ComponentX.class);
}
compBinder.addBinding().to(ComponentY.class);
compBinder.addBinding().to(ComponentZ.class);
}
}
This also seems a little clunky because it requires the use of a child injector etc.
Again, is there a better way?
Maybe I could use Netflix's Governator (https://github.com/Netflix/governator/wiki/Configuration-Mapping) to inject Configuration values into my module (not sure if that is possible or not)?
How do other people approach this problem?
The applications I've been working with recently have a properties file (or other configuration) that is used to decide which parts of the application are relevant. Our typical approach is parse those properties immediately (just to a Properties object) and construct the application module(s) from that, and they will then conditionally include other modules based on the values specified.
In a couple of places, this has grown into an "init parameters" set, with an enumeration of possible parameters:
enum InitParam {
PricesQueue("prices.queue")
}
Each enum instance is related to a property key and there is a method to get a basic string value for each parameter from Properties:
boolean presentIn(Properties props) { return props.containsKey(propertyKey); }
String valueIn(Properties props) { return props.getProperty(propertyKey); }
So this can be used like so:
public AppModule extends AbstractModule {
private final Properties config;
protected void configure() {
if (InitParam.PricesQueue.presentIn(config)) {
install(new PricesQueueConsumerModule(config));
}
}
}
Additionally, there is a module to bind all the values in the config properties to String, Optional<String> etc, allowing:
#Inject
public PricesQueueConsumer(#FromInitParam(InitParam.PricesQueue) String queueName) {
}
This will trap the queue consumer being referenced when the configuration isn't available (the module won't bind a string if the value isn't present in the config file) while still allowing the behaviour for when the value isn't present to be deferred to later (by injecting Optional<String> instead)
So this is somewhat similar to your second approach, except that I'd not considered the using-Guice-to-inject-Guice-modules approach, which seems a bit convoluted. Although probably it's essentially the same. Maybe rather than a parent/child injector you could simply create a "bootstrapping" injector to build your top-level application module, and then use that to build a completely separate injector?

Java Properties File binding to Java Interface

With GWT you have stuff like this:
public interface LoginConstants extends Constants {
#DefaultStringValue("Wellcome to my super app")
#Key("appDescription")
String appDescription();
#DefaultStringValue("Ok")
#Key("okButtonLabel")
String okButtonLabel();
}
Then you can use from your classes doing GWT.create(LoginConstant.class), in this way the interface is backed by dynamic implementation that, when I call loginConstants.appDescription() returns the value contained from a property file using the #Key annotation to reference the key in the property file. If the property file misses the property, then de #DefaultStringValue is returned. This is used for internationalization, but can possibly work also for configuration.
But with GWT, this is meant to be used on the client side (ie. translated to JavaScript), and for i18n, not for configuration.
But, I find this idea very convenient also for configuration handling.
I wonder if somebody knows a framework to do a similar thing on the server side, without necessarily bind your code to GWT. ie. if there is any library that implements this kind of logic specifically designed for the configuration handling. I am not aware of anything like this.
Reference to the feature in GWT: https://developers.google.com/web-toolkit/doc/latest/DevGuideI18nConstants
I implemented my own solution to the question:
BASIC USAGE
The approach used by OWNER APIs, is to define a Java interface
associated to a properties file.
Suppose your properties file is defined as ServerConfig.properties:
port=80
hostname=foobar.com
maxThreads=100
To access this property you need to define a convenient Java interface
in ServerConfig.java:
public interface ServerConfig extends Config {
int port();
String hostname();
int maxThreads();
}
We'll call this interface the Properties Mapping Interface or just
Mapping Interface since its goal is to map Properties into an easy to
use a piece of code.
Then, you can use it from inside your code:
public class MyApp {
public static void main(String[] args) {
ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
System.out.println("Server " + cfg.hostname() + ":" + cfg.port() +
" will run " + cfg.maxThreads());
}
}
But this is just the tip of the iceberg.
Continue reading here: Basic usage || Website || Github
I still have a couple of features in mind, but the current implementation goes a little forward than the basic functionalities described in the questions.
I need to add samples and documentation.
I loved the idea so much that I quickly assembled some code using Java Dynamic proxies.
So basically you create an interface with relevant methods and annotate them with #Key, #DefaultStringValue annotations.
Below is the sample Java code:
Main.java
package net.viralpatel;
import net.viralpatel.annotations.DefaultStringValue;
import net.viralpatel.annotations.Key;
interface LoginConstants extends Constants {
#DefaultStringValue("Wellcome to my super app")
#Key("appDescription")
String appDescription();
#DefaultStringValue("Ok")
#Key("okButtonLabel")
String okButtonLabel();
}
public class Main {
public static void main(String[] args) {
LoginConstants constants = DynamicProperty.create(LoginConstants.class);
System.out.println(constants.appDescription());
System.out.println(constants.okButtonLabel());
}
}
Also the property file in background that we load is
config.property
okButtonLabel=This is OK
Just execute the Main java class, following output will be displayed:
Output:
Wellcome to my super app
This is OK
Here is the rest of code: http://viralpatel.net/blogs/dynamic-property-loader-using-java-dynamic-proxy-pattern/
You could mimic that with spring (but I'm not sure it's worth it):
#Component
public class SomeBean {
#Value("${appDescription:Wellcome to my super app}")
private String appDescription;
#Value("${okButtonLabel:Ok}")
private String okButtonLabel;
// accessors
}
with a PropertyPlaceHolderConfigurer.
I would like to consider the CDI as the following :-
The Qualifier
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
ElementType.TYPE
})
#Documented
public #interface MessageTemplate {
#Nonbinding
String baseName();
#Nonbinding
Locale locale() default Locale.ENGLISH;
#Nonbinding
String key();
}
The Producer
public class CustomizedProducer {
#Produces
#MessageTemplate(baseName = "",
key = "")
public String createMessageTemplate(final InjectionPoint ip) {
MessageTemplate configure = null;
ResourceBundle bundle = null;
try{
configure = ip.getAnnotated().getAnnotation(MessageTemplate.class);
bundle = ResourceBundle.getBundle(configure.baseName(),
configure.locale());
return bundle.getString(configure.key());
} finally{
configure = null;
bundle = null;
}
}
}
The Service Configure
public class MyServiceConfigure {
#Inject
#MessageTemplate(baseName = "com.my.domain.MyProp",
key = "appDescription")
private String appDescription;
#Inject
#MessageTemplate(baseName = "com.my.domain.MyProp",
key = "okButtonLabel")
private String okButtonLabel;
//Getter
}
The working class
public class MyService {
#Inject
private MyServiceConfigure configure;
public void doSomething() {
System.out.println(configure.getAppDescription());
System.out.println(configure.getOkButtonLabel());
}
}
Regarding to the coding above you may use the java.util.Properties instead of the java.util.ResourceBundle and provide the default member to the Qualifier as well.
If you are running these under the JavaEE 6, the CDI is already enable for you. Just put the empty beans.xml to the META-INF or WEB-INF. If you are running under the Java SE you may need a bit further work as mentioned at the Weld web site and its documentation.
I'm using the CDI as a main part of my current production project and it works quite well.
EDITED:-
The good point to use the CDI is the Scope, we may produce the #MessageTemplate as the #ApplicationScope,#SessionScoped, #RequestScoped, #ConversationScoped or the pseudo-scope as #Singleton or #Depenendent
If you annotate the MyServiceConfigure as #Named, it is ready to use at the JSF as well.

Categories

Resources