I'm trying to use the Togglz library which allows you to wrap your application logic and be able to toggle it ON or OFF, with some advanced strategies. I am going through the Spring Boot documentation for it, and although its quite concise, I'm finding it missing pieces of information that are not allowing me to test this properly.
reference:
https://www.togglz.org/documentation/spring-boot-starter.html
I am running a Spring Boot 2.4.5 version project and this documentation says to import the dependency, which I did:
<dependency>
<groupId>org.togglz</groupId>
<artifactId>togglz-spring-boot-starter</artifactId>
<version>2.6.1.Final</version>
</dependency>
The documentation then states you can use an auto-configuration class on your #RestController, like
#Controller
public class MyClass {
private FeatureManager manager;
public MyClass(FeatureManager manager) {
this.manager = manager;
}
#RequestMapping("/")
public ResponseEntity<?> index() {
if (manager.isActive(HELLO_WORLD)) {
...
}
}
}
Here is already where I had some questions that I didn't see explained, first, that are passing a enum "HELLO_WORLD" as an argument to this isActive() function on the FeatureManager. I don't see how they are injecting this into the method/class. They do show how to declare the feature ENUM in the yaml, though, this is not referencing the "HELLO_WORLD" that was passed into the isActive() method mentioned previously, i.e. :
togglz:
features:
FOO:
enabled: true
BAR:
enabled: false
Further down the documentation, they finally do reference this HELLO_WORLD enum, but I tried adding this to my application.yaml and I can't seem to figure out how they are injecting these Feature Enums into these methods:
togglz:
enabled: true # Enable Togglz for the application.
features: # The feature states. Only needed if feature states are stored in application properties.
HELLO_WORLD:
enabled: true
The documentation does explain how to create an enum class for these features, but they explictly list it as an alternative to defining it in the yaml file
public enum MyFeatures implements Feature {
#EnabledByDefault
#Label("First Feature")
FEATURE_ONE,
#Label("Second Feature")
FEATURE_TWO;
}
#Bean
public FeatureProvider featureProvider() {
return new EnumBasedFeatureProvider(MyFeatures.class);
}
I tried this also, and I just got more Bean exception errors when I try to run the Application, i.e.
Description:
Parameter 2 of method featureManager in org.togglz.spring.boot.autoconfigure.TogglzAutoConfiguration$FeatureManagerConfiguration required a bean of type 'org.togglz.core.user.UserProvider' that could not be found.
Action:
Consider defining a bean of type 'org.togglz.core.user.UserProvider' in your configuration.
Can anyone who has successful used this library provide input how to set a simple feature toggle ? Ultimately, I want to be able turn ON/OFF this feature while the application using a RELEASE DATE activation strategy i.e. 2021-06-30 00:00:00 such that I can have the toggle activate based on a datetime.
reference: https://www.togglz.org/documentation/activation-strategies.html
Can this be done in the yaml?
If you don't want to use an Enum you will have to inject the FeatureProvider that was auto-configured and call featureProvider.getFeatures() to get hold of all available features.
You can then check their state with the FeatureManager.
I agree that this is not obvious from the documentation.
It should be possible to configure the activation strategy via your application.yml too.
See the example section "Application Properties" at the very end of https://www.togglz.org/documentation/spring-boot-starter.html.
It should look like this:
togglz.features.FOO:
enabled: true
strategy: release-date
param:
date: ..
time: ..
I have Implemented the library successfully only change required in the above mentioned code or for the exception required a bean of type org.togglz.core.user.UserProvider that could not be found is to add one more bean UserProvider.
#Bean
public UserProvider getUserProvider() {
return new ServletUserProvider("admin");
}
Related
I am trying to implement a project using jobrunr. I have a use case where a service I have written should be triggered once the maximum retries are done for a job. I tried achieving the same using this answer as reference. The filter logic is triggered once a job fails but the dependency I include (which has my logic) is returning a null point exception(java.lang.NullPointerException: Cannot invoke "com.project.service.ScheduleHistoryService.someFunc()" because "this.service" is null). I am able to inject the same service file using #Autowire in my other components.
What am I doing wrong here?
I am using jobrunr version 5.1.4.
Attached is a screenshot of the sample code:enter image description here
Injecting services in the filters is only possible in the Pro version of JobRunr.
My hack / workaround for this is injecting the Service in the regarding Configuration and passing it to the constructor of a CustomRetryFilter:
#Configuration
public class JobRunrConfig {
#Bean
public BackgroundJobServer backgroundJobServer(
StorageProvider storageProvider, JsonMapper jobRunrJsonMapper, JobActivator jobActivator, BackgroundJobServerConfiguration backgroundJobServerConfiguration, JobRunrProperties properties,
ApplicationEventPublisher applicationEventPublisher) {
BackgroundJobServer backgroundJobServer = new BackgroundJobServer(
storageProvider, jobRunrJsonMapper, jobActivator, backgroundJobServerConfiguration);
backgroundJobServer.setJobFilters(
Collections.singletonList(
new CustomRetryFilter(
applicationEventPublisher,
properties.getJobs().getDefaultNumberOfRetries(),
properties.getJobs().getRetryBackOffTimeSeed())));
backgroundJobServer.start();
return backgroundJobServer;
}
}
*I need to invoke a spring service from my java class, how would i set the active profile dynamically for spring service.
here's the code
java code
public void abc() {
AccountDetailService service = new AccountDetailService();
service.getAccountDetails();
}
AccountDetailService
#Profile
#Log
#Component
private void getAccountDetails() {
String filename=environment.getProperty("fileName");
accountDaoImpl.getDetails(filename);
}
i have various profiles like dev,qa and prod
how would i pass active profiles from my java class when invoking spring service.*
You don't need to pass any profiles to your service. Simply, create configurations for different profiles. These configurations will provide the correct service instance for given profile. Here you have an example of such configuration class:
#Configuration
#Profile(value = "test")
public class ServiceTestConfig
{
#Bean
public Service service()
{
return new TestService();
}
}
Create another configuration with other #Profile annotation and Spring will automatically create proper instances. You can set active profile in many ways, the simplest is to change the spring.profiles.active property in your application.properties. Refer to Spring documentation to learn more about profiles:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
Update
If you really need to pass your active profile to your service in runtime, you can inject Environment instance and call getActiveProfiles(). See the javadoc: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/env/Environment.html. Just keep in mind it's not the way profiles should be used in Spring. The example I provided before is considered the best practice.
I use spring-boot-starter-data-solr and would like to make use of the schmea cration support of Spring Data Solr, as stated in the documentation:
Automatic schema population will inspect your domain types whenever the applications context is refreshed and populate new fields to your index based on the properties configuration. This requires solr to run in Schemaless Mode.
However, I am not able to achieve this. As far as I can see, the Spring Boot starter does not enable the schemaCreationSupport flag on the #EnableSolrRepositories annotation. So what I tried is the following:
#SpringBootApplication
#EnableSolrRepositories(schemaCreationSupport = true)
public class MyApplication {
#Bean
public SolrOperations solrTemplate(SolrClient solr) {
return new SolrTemplate(solr);
}
}
But looking in Wireshark I cannot see any calls to the Solr Schema API when saving new entities through the repository.
Is this intended to work, or what am I missing? I am using Solr 6.2.0 with Spring Boot 1.4.1.
I've run into the same problem. After some debugging, I've found the root cause why the schema creation (or update) is not happening at all:
By using the #EnableSolrRepositories annotation, an Spring extension will add a factory-bean to the context that creates the SolrTemplate that is used in the repositories. This template initialises a SolrPersistentEntitySchemaCreator, which should do the creation/update.
public void afterPropertiesSet() {
if (this.mappingContext == null) {
this.mappingContext = new SimpleSolrMappingContext(
new SolrPersistentEntitySchemaCreator(this.solrClientFactory)
.enable(this.schemaCreationFeatures));
}
// ...
}
Problem is that the flag schemaCreationFeatures (which enables the creator) is set after the factory calls the afterPropertiesSet(), so it's impossible for the creator to do it's work.
I'll create an issue in the spring-data-solr issue tracker. Don't see any workaround right now, other either having a custom fork/build of spring-data or extend a bunch of spring-classes and trying to get the flag set before by using (but doubt of this can be done).
I've created a lot of common small bean-definition containers (#Configuration) which I use to rapidly develop applications with Spring Boot like:
#Import({
FreemarkerViewResolver.class, // registers freemarker that auto appends <#escape etc.
ConfigurationFromPropertiesFile.class, // loads conf/configuration.properties
UtfContentTypeResponse.class, // sets proper Content-language and Content-type
LocaleResolverWithLanguageSwitchController // Locale resolver + switch controller
);
class MySpringBootApp ...
For example, one of such #Configurations can set up session storage for locale cookie with web controller to switch to selected language etc.
They are very fun to work with and reuse, but it would be really great to make it parametrized, which could allow lot more reusege. I mean something like:
Pseudo code:
#Imports( imports = {
#FreemarkerViewResolver( escapeHtml = true, autoIncludeSpringMacros = true),
#ConfigurationFromProperties( path = "conf/configuration.properties" ),
#ContentTypeResponse( encoding = "UTF-8" ),
#LocaleResolver( switchLocaleUrl = "/locale/{loc}", defaultLocale = "en"
})
So, I basically mean "configurable #Configurations". What would be the best way to make the configuration that way?
Maybe something more like this (again, pseudo code):
#Configuration
public class MyAppConfiguration {
#Configuration
public FreemarkerConfiguration freemarkerConfiguration() {
return FreemarkerConfigurationBuilder.withEscpeAutoAppend();
}
#Configuration
public ConfigurationFromPropertiesFile conf() {
return ConfigurationFromPropertiesFile.fromPath("...");
}
#Configuration
public LocaleResolverConfigurator loc() {
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale("en").withSwitchUrl("/switchlocale/{loc}");
}
Let me quote Spring Boot Reference Guide - Externalized Configuration:
"Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments."
In my opinion the customization is not done at import time via annotation parameters like in your 2nd pseudo code block, instead the customization happens at run time e.g. in the configuration classes. Let me adapt your 3rd code block (only one function):
#Configuration
public class MyAppConfiguration {
#Autowired
private Environment env;
// Provide a default implementation for FreeMarkerConfigurer only
// if the user of our config doesn't define her own configurer.
#Bean
#ConditionalOnMissingBean(FreeMarkerConfigurer.class)
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setTemplateLoaderPath("/WEB-INF/views/");
return result;
}
...
#Bean
public LocaleResolverConfigurator loc() {
String defaultLocale = env.getProperty("my.app.config.defaultlocale", "en");
String switchLocale = env.getProperty("my.app.config.switchlocale", "/switchlocale/{loc}");
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale(defaultLocale).withSwitchUrl(switchLocale);
}
For LocaleResolverConfigurator the configuration is read from the environment, meaningful default values are defined. It is easy to change the default value(s) by providing a different value for a config parameter in any of the supported ways (documented in the first link) - via command line or a yaml file. The advantage over annotation parameters is that you can change the behavior at run time instead of compile time.
You could also inject the config parameters (if you prefer to have them as instance variable) or use a lot of other conditions, e.g. #ConditionalOnMissingBean, #ConditionalOnClass, #ConditionalOnExpression and so on. For example with #ConditionalOnClass you could check if a particular class is on your class path and provide a setting for the library identified by this class. With #ConditionalOnMissingClass you could provide an alternative implementation. In the example above I used ConditionalOnMissingBean to provide a default implementation for the FreeMarkerConfigurer. This implementation is only used when no FreeMarkerConfigurer bean is available thus can be overridden easily.
Take a look at the starters provided by Spring Boot or the community. A good read is also this blog entry. I learned a lot from spring-boot-starter-batch-web, they had an article series in a German Java magazine, but parts are also online, see Boot your own infrastructure – Extending Spring Boot in five steps (MUST READ) and especially the paragraph "Make your starter configurable by using properties".
Though I like the idea of having imports be parameterized, I think that as it stands now using #Import and #Configuration not a good fit.
I can think of two ways to use dynamic configurations, that don't rely on PropertySource style configuration.
Create a custom #ImportConfig annotation and annotation processor that accepts configuration properties that are hard-coded into the generated source files.
Use a BeanFactoryPostProcessor or BeanPostProcessor to add or manipulate your included beans respectively.
Neither is particularly simple IMO, but since it looks like you have a particular way of working. So it could be worth the time invested.
I've been trying to get a sample JMX MXBean working in a Spring-configured webapp, but any basic attributes on the MXBean are coming up as UNDEFINED when I connect with jconsole.
Java interface/classes:
public interface IJmxBean { // marker interface for spring config, see below
}
public interface MgmtMXBean { // lexical convention for MXBeans - mgmt interface
public int getAttribute();
}
public class Mgmt implements IJmxBean, MgmtMXBean { // actual JMX bean
private IServiceBean serviceBean; // service bean injected by Spring
private int attribute = 0;
#Override
public int getAttribute() {
if(serviceBean != null) {
attribute = serviceBean.getRequestedAttribute();
}
return attribute;
}
public void setServiceBean(IServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
}
Spring JMX config:
<beans>
<context:component-scan base-package="...">
<context:include-filter type="assignable" expression="...IJmxBean" />
</context:component-scan>
<context:mbean-export />
</beans>
Here's what I know so far:
The element is correctly instantiating a bean named "mgmt". I've got logging in a zero-argument public constructor that indicates it gets constructed.
is correctly automatically detecting and registering the MgmtMXBean interface with my Tomcat 6.0 container. I can connect to the MBeanServer in Tomcat with jconsole and drill down to the Mgmt MXBean.
When examining the MXBean, "Attribute" is always listed as UNDEFINED, but jconsole can tell the correct type of the attribute. Further, hitting "Refresh" in jconsole does not actually invoke the getter method of "Attribute"- I have logging in the getter method to indicate if it is being invoked (similar to the constructor logging that works) and I see nothing in the logs.
At this point I'm not sure what I'm doing wrong. I've tried a number of things, including constructing an explicit Spring MBeanExporter instance and registering the MXBean by hand, but it either results in the MBean/MXBean not getting registered with Tomcat's MBean server or an Attribute value of UNDEFINED.
For various reasons, I'd prefer not to have to use Spring's #ManagedResource/#ManagedAttribute annotations.
Is there something that I'm missing in the Spring docs or MBean/MXBean specs?
ISSUE RESOLVED: Thanks to prompting by Jon Stevens (above), I went back and re-examined my code and Spring configuration files:
Throwing an exception in the getAttribute() method is a sure way to get "Unavailable" to show up as the attribute's value in JConsole. In my case:
The Spring JMX config file I was using was lacking the default-autowire="" attribute on the root <beans> element;
The code presented above checks to see if serviceBean != null. Apparently I write better code on stackoverflow.com than in my test code, since my test code wasn't checking for that. Nor did I have implements InitializingBean or #PostConstruct to check for serviceBean != null like I normally do on almost all the other beans I use;
The code invoking the service bean was before the logging, so I never saw any log messages about getter methods being entered;
JConsole doesn't report when attribute methods throw exceptions;
The NPE did not show up in the Tomcat logs.
Once I resolved the issue with serviceBean == null, everything worked perfectly. Regardless, +1 to Jon for providing a working demo, since there are literally 50 different ways to configure MBeans/MXBeans within Spring.
I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache.
It has examples for both EntityManager based access and DAO access (including transactions!). It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. There is even a SpringMVC based example servlet using annotations. Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine.
It isn't documented yet, but I'll get to that soon. Take a look at the configuration files and source code and it should be pretty clear.
The motivation behind this is that I got tired of all of the crazy blog posts with 50 different ways to set things up and finally made a single simple source that people can work from. It is up on github so feel free to fork the project and do whatever you want with it.
https://github.com/lookfirst/fallback