Set the cron expression value dynamically - spring

I want to set the cron expression value dynamically using controller. This is my code snippet.
#Service
#EnableScheduling
#Configuration
#PropertySources({
#PropertySource("classpath:properties/cron.properties")
})
public class SchedulerServiceImpl implements SchedulerService{
private final static Logger log = LoggerFactory.getLogger(SchedulerServiceImpl.class);
#Value( "${cron.expression}")
private String cronValue;
#Scheduled(cron = "${cron.expression}")
public void getTweetsFromAPI(){
log.info("Trying to make request for get Tweets through TweetController.");
log.debug("----------------Executing cron.expression {}----------------",cronValue);
}
}
how can I set the cron.expression value dynamically after the deployment. If I used a controller and replace the properties file existing value, Does it work?

Logically your question is wrong. Cron expression is for scheduling a method for a specified time i.e. the method will be executed at a specified time say 12 noon everyday.
Now if you are dynamically changing the cron expression value that means you are controlling the execution of this method and not the scheduler which is wrong.
You can do something like below:-
#Value( "${cron.expression1}")
private String cron1;
#Value( "${cron.expression2}")
private String cron2;
#Scheduled(cron=cron1)
public void method1(){
//call to method
this.method();
}
#Scheduled(cron=cron2)
public void method2(){
//call to method
this.method();
}
private void method(){
//Your scheduling logic goes here
}
Here you are reusing the method() to be scheduled at two different times.

Related

SpEL unable to access the instance variable

I am trying to make use of Spring Boot AutoConfigured #EnableScheduling to create a cron job. Job triggers perfectly fine if the fixedRateString is hardcoded. However it does not succeed if I try to make use of SpEL to supply the value.
//Bean Instantiation in JavaConfig
#Bean
public AlertbotJob2 getAlertJob2() {
AlertbotJob2 alertbotJob2 = new AlertbotJob2("alertId", "alertName", "alertSubject", "6000", true);
return alertbotJob2;
}
Bean Definition
public class AlertbotJob2 {
String alertId;
String alertName;
String alertSubject;
public String cronPattern;
boolean isActive;
public AlertbotJob2() {
}
public AlertbotJob2(String alertId, String alertName, String alertSubject, String cronPattern, boolean isActive) {
super();
this.alertId = alertId;
this.alertName = alertName;
this.alertSubject = alertSubject;
this.cronPattern = cronPattern;
this.isActive = isActive;
}
#Scheduled(initialDelay = 60000, fixedRateString = "#{this.cronPattern}")
public void doTheJob() {
System.out.println("DoSomething");
}
}
Exception is :: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'this' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
I am just trying to set the trigger values dynamically. Please help.
You can't use this there.
You have to do it by bean name #someBean.cronPattern.
I don't think you can use SpEL like this.
Maybe the better approach is to define your cron pattern in some property like:
myapp.scheduler.cronPattern=...
And then use the following syntax:
#Scheduled(cron = "${myapp.scheduler.cronPattern}")
public void doTheJob() {
...
}
Solution 1: use the hardcoded pattern if its already known
#Scheduled(cron = "0 0/5 * * * MON-FRI")
Solution 2: If the pattern is available in some property
#Scheduled(cron = "${myapp.scheduler.cronPattern}")
Solution 3 : This approach does not use annotation. Instead the Beans are just implemented as runnables And then override the SchedulingConfigurer.configureTasks() to dynamically set the schedules using taskRegistrar.addCronTask(urRunnableJob, urCronPatternFromDB); . Cron pattern is stored in database table, initialize it as properties as below.
class AppConfig {
#Autowired
ConfigurableEnvironment environment;
private void initPropertiesMap() {
environment.getPropertySources()
.addLast(new MapPropertySource("myAppProperties", myAppProperties.getProperties())); // Prior DAO call required to read the configs from database
}
#PostConstruct
public void postConstruct() {
initPropertiesMap();
}
}
you can try this:
e.g #Value("#{T(com.someName.ClassName).cronPattern}")
I reference this Accessing static variable in spring annotations using spel

How use dynamic scheduler in spring framework?

I'm using Spring Security 4.2. I want to block user for 15 minutes after 3 login failure. For blocking user, change user status to BLOCK. I want to return it to ACTIVE after 15 minutes.
I need a dynamic task scheduler that run after 15 minutes and change user status.
Important: I don't want to run method every 15 minutes, I want to run method after 15 minutes if needed.
How do I implement it?
I have some approach. I guess that you should use User class like following:
class User {
// ...
private Status status;
private LocalDateTime lastUpdated;
// ...
}
So that, you should update status and lastUpdated fields during a user blocking.
#Service
public class UserService {
private UserRepository userRepository;
#Transactional
public void block(User user) {
user.setStatus(Status.BLOCKED);
user.setLastUpdated(LocalDateTime.now());
userRepository.save(user);
}
}
After that you can use Spring Scheduler which run every minute and finds users that were blocked 15 minutes ago and sets status to ACTIVE.
#Component
public class UserActivator {
private boolean activateUsers = false;
#Scheduled("0 * * * * *")
public void activateUsers() {
if (activateUsers) {
// find users that were deactivated 15 minutes ago and change status to active
}
}
}
Do not forget to add #EnableScheduling to your Spring config.
You might have table defination for doing this. But this is how I do it generally.
Create a seprate table to maintain the failure count.
Then on basis of this you can check in your service / controller layer.
#Controller
public Class LoginController{
#Autowired
private UserLoginFailureRepository userLoginFailureRepository;
#Autowired
private UserRepostory userRepository;
#Transactional
public void login(){
UserLoginFailure loginFailure = userLoginFailureRepository.getUserLoginfailureDetails(username);
if (loginFailure != null) {
loginFailure.setFailureCount(1l + loginFailure.getFailureCount());
if (loginFailure.getFailureCount() > loginFailureCount) {
// block the user.
User user = userRepository.getUser(username);
user.setStatus(BLOCK);
user.setModificationDate(LocalDateTime.now());
}
}
}
}
Create a seprate Scheduled Job to check and update User Status and reset UserLoginFailure count.
#Component
public class userPasswordResetJob{
#Autowired
private UserLoginFailureRepository userLoginFailureRepository;
#Autowired
private UserRepostory userRepository;
#Scheduled(cron = "${USER_STATUS_JOB}")
public void loginDay() throws Exception{
// Have your logic to update the User status and reset the failure count.
}
}

Jackson #JsonFilter is not getting applied when used at field or method level

I am using Spring version 4.3.3 and Jackson version 2.8.3. I am trying to filter out specific fields from an entity bean based on some custom logic that is determined at runtime. The #JsonFilter seems ideal for this type of functionality. The problem is that when I put it at the field or method level, my custom filter never gets invoked. If I put it at the class level, it gets invoked just fine. I don't want to use it at the class level though since then I would need to separately maintain the list of hardcoded field names that I want to apply the logic to. As of Jackson 2.3, the ability to put this annotation at the field level is supposed to exist.
Here is the most basic custom filter without any custom logic yet:
public class MyFilter extends SimpleBeanPropertyFilter {
#Override
protected boolean include(BeanPropertyWriter beanPropertyWriter) {
return true;
}
#Override
protected boolean include(PropertyWriter propertyWriter) {
return true;
}
}
Then I have the Jackson ObjectMapper configuration:
public class MyObjectMapper extends ObjectMapper {
public MyObjectMapper () {
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("myFilter", new MyFilter());
setFilterProvider(filterProvider);
}
}
Then finally I have my entity bean:
#Entity
public class Project implements Serializable {
private Long id;
private Long version;
#JsonFilter("myFilter") private String name;
#JsonFilter("myFilter") private String description;
// getters and setters
}
If I move the #JsonFilter annotation to the class level where #Entity is, the filter at least gets invoked, but when it is at the field level like in the example here, it never gets invoked.
I have the same need but after examining the unit tests I discovered that this is not the use-case covered by annotating a field.
Annotating a field invokes a filter on the value of the field not the instance containing the field. For example, imagine you have to classes, A and B, where A contains a field of type B.
class A {
#JsonFilter("myFilter") B foo;
}
Jackson applies "myFilter" to the fields in B not in A. Since your example contains fields of type String, which has no fields, Jackson never invokes your filter.
I have a need to exclude certain fields based on the caller's permissions. For example, an employee's profile may contain his taxpayer id, which is considered sensitive information and should only be serialized if the caller is a member of the Payrole department. Since I'm using Spring Security, I wish to integrate Jackson with the current security context.
public class EmployeeProfile {
private String givenName;
private String surname;
private String emailAddress;
#VisibleWhen("hasRole('PayroleSpecialist')")
private String taxpayerId;
}
The most obvious way to do this is to Jackson's filter mechanism but it has a few limitations:
Jackson does not support nested filters so adding an access filter prohibits using filters for any other purpose.
One cannot add Jackson annotations to existing, third-party classes.
Jackson filters are not designed to be generic. The intent is to write a custom filter for each class you wish to apply filtering. For example, I you need to filter classes A and B, then you have to write an AFilter and a BFilter.
For my use-case, the solution is to use a custom annotation introspector in conjunction with a chaining filter.
public class VisibilityAnnotationIntrospector extends JacksonAnnotationIntrospector {
private static final long serialVersionUID = 1L;
#Override
public Object findFilterId(Annotated a) {
Object result = super.findFilterId(a);
if (null != result) return result;
// By always returning a value, we cause Jackson to query the filter provider.
// A more sophisticated solution will introspect the annotated class and only
// return a value if the class contains annotated properties.
return a instanceof AnnotatedClass ? VisibilityFilterProvider.FILTER_ID : null;
}
}
This is basically a copy SimpleBeanProvider that replaces calls to include with calls to isVisible. I'll probably update this to use a Java 8 BiPredicate to make the solution more general but works for now.
This class also takes another filter as an argument and will delegate to it the final decision on whether to serialize the field if the field is visible.
public class AuthorizationFilter extends SimpleBeanPropertyFilter {
private final PropertyFilter antecedent;
public AuthorizationFilter() {
this(null);
}
public AuthorizationFilter(final PropertyFilter filter) {
this.antecedent = null != filter ? filter : serializeAll();
}
#Deprecated
#Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
if (isVisible(bean, writer)) {
this.antecedent.serializeAsField(bean, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(bean, jgen, provider);
}
}
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(pojo, writer)) {
this.antecedent.serializeAsField(pojo, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
#Override
public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(elementValue, writer)) {
this.antecedent.serializeAsElement(elementValue, jgen, provider, writer);
}
}
private static boolean isVisible(Object pojo, PropertyWriter writer) {
// Code to determine if the field should be serialized.
}
}
I then add a custom filter provider to each instance of ObjectMapper.
#SuppressWarnings("deprecation")
public class VisibilityFilterProvider extends SimpleFilterProvider {
private static final long serialVersionUID = 1L;
static final String FILTER_ID = "dummy-filter-id";
#Override
public BeanPropertyFilter findFilter(Object filterId) {
return super.findFilter(filterId);
}
#Override
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
if (FILTER_ID.equals(filterId)) {
// This implies that the class did not have an explict filter annotation.
return new AuthorizationFilter(null);
}
// The class has an explicit filter annotation so delegate to it.
final PropertyFilter antecedent = super.findPropertyFilter(filterId, valueToFilter);
return new VisibilityPropertyFilter(antecedent);
}
}
Finally, I have a Jackson module that automatically registers the custom annotaion introspector so I don't have to add it to each ObjectMapper instance manually.
public class FieldVisibilityModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public FieldVisibilityModule() {
super(PackageVersion.VERSION);
}
#Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
// Append after other introspectors (instead of before) since
// explicit annotations should have precedence
context.appendAnnotationIntrospector(new VisibilityAnnotationIntrospector());
}
}
There are more improvements that can be made and I still have more unit tests to write (e.g., handling arrays and collections) but this is the basic strategy I used.
You can try this approach for the same purpose:
#Entity
#Inheritance(
strategy = InheritanceType.SINGLE_TABLE
)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
length = 2
)
#Table(
name = "project"
)
#JsonTypeInfo(
use = Id.CLASS,
include = As.PROPERTY,
property = "#class"
)
#JsonSubTypes({
#Type(
value = BasicProject.class,
name = "basicProject"
),
#Type(
value = AdvanceProject.class,
name = "advanceProject"
)})
public abstract class Project {
private Long id;
private Long version;
}
#Entity
#DiscriminatorValue("AD")
public class AdvanceProject extends Project {
private String name;
private String description;
}
#Entity
#DiscriminatorValue("BS")
public class BasicProject extends Project {
private String name;
}
I don't think you will make it work. I was trying and these are results of my investigation, maybe it will be helpful.
First of all, as #Faron noticed, the #JsonFilterannotation is applied for the class being annotated not a field.
Secondly, I see things this way. Let's imagine, somewhere in Jackson internals you are able to get the actual field. You can figure out if there is the annotation using Java Reflection API. You can even get the filter name. Then you get to the filter and pass the field value there. But it happens at runtime, how will you get the corresponding JsonSerializer of the field type if you decide to serialize the field? It is impossible because of type erasure.
The only alternative I see is to forget about dynamic logic. Then you can do the following things:
1) extend JacksonAnnotationIntrospector (almost the same as implement AnnotationIntrospector but no useless default code) overriding hasIgnoreMarker method. Take a look at this answer
2) criminal starts here. Kinda weird way taking into account your initial goal but still: extend BeanSerializerModifier and filter out fields there. An example can be found here. This way you can define serializer that actually doesn't serialize anything (again, I understand how strange it is but maybe one will find it helpful)
3) similar to the approach above: define useless serializer based on BeanDescription implementing ContextualSerializer's createContextual method. The example of this magic is here
Thanks to this really good blog, I was able to use #JsonView to filter out specific fields from an entity bean based on some custom logic that is determined at runtime.
Since the #JsonFilter does not apply for the fields within a class, I found this to be a cleaner workaround.
Here is the sample code:
#Data
#AllArgsConstructor
public class TestEntity {
private String a;
#JsonView(CustomViews.SecureAccess.class)
private Date b;
#JsonView(CustomViews.SecureAccess.class)
private Integer c;
private List<String> d;
}
public class CustomViews {
public static interface GeneralAccess {}
public static interface SecureAccess {}
public static class GeneralAccessClass implements GeneralAccess {}
public static class SecureAccessClass implements SecureAccess, GeneralAccess {}
public static Class getWriterView(final boolean hasSecureAccess) {
return hasSecureAccess
? SecureAccessClass.class
: GeneralAccessClass.class;
}
}
#Test
public void test() throws JsonProcessingException {
final boolean hasSecureAccess = false; // Custom logic resolved to a boolean value at runtime.
final TestEntity testEntity = new TestEntity("1", new Date(), 2, ImmutableList.of("3", "4", "5"));
final ObjectMapper objectMapper = new ObjectMapper().enable(MapperFeature.DEFAULT_VIEW_INCLUSION);
final String serializedValue = objectMapper
.writerWithView(CustomViews.getWriterView(hasSecureAccess))
.writeValueAsString(testEntity);
Assert.assertTrue(serializedValue.contains("a"));
Assert.assertFalse(serializedValue.contains("b"));
Assert.assertFalse(serializedValue.contains("c"));
Assert.assertTrue(serializedValue.contains("d"));
}

Spring Quatrz Dynamic Annotation Based Java Config

Sorry i had to ask it over here as i searched a lot tried many things but failed to achieve the result.
So what i am trying to do is i have a service which give me the list of the Jobs along with the interval at which they need to run so , what i want to do is loop over the list of the jobs and schedule them using Spring Quartz and i want to do them using the java config and not XML based .
This application will be a web application which will be running on a server, a maven project.
I found result and the are mainly using the Custom Annotation . Is there any sample or example which i can try or use.?
The problem is we are very new to all this and none of us have a idea how to proceed with this so any help is very appreciated.
Thanks in advance,
Vishesh
Have your #Confugation class implement SchedulingConfigurer. This allows you to schedule tasks programmatically. For example
#Configuration
#EnableScheduling
public class TaskConfiguration implements SchedulingConfigurer{
#Autowired
private TaskDao taskDao; // implement TaskDao to read tasks from DB
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar){
List<Task> tasks = taskDao.findAllTasks();
for(Task task : tasks){
Runnable taskJob = createTaskJob(task); // create task by reflection
taskRegistrar.addCronTask(taskJob , task.getCronExpression());
}
}
}
public class Task implements Serializable{
private String cronExpression;
private String jobClass;
//getters and setters
}
#ekem chitsiga workaround will also do. Following is another alternate to run your cron jobs based on the cron epressions you set.
#Configuration
#EnableScheduling
public class Scheduler {
private static final Logger logger = LoggerFactory
.getLogger(Scheduler.class);
#Autowired
private JobRepository jobRepository;
/*
* Cron expression ="0 0/1 * 1/1 * ?" for every minute
*/
#Scheduled(cron = "0 0/1 * 1/1 * ?")
public void sendNotification() {
logger.info("Initializing Cron job scheduler....");
//Do you scheduler specific work here
}
}

Spring: How to inject a value to static field?

With this class
#Component
public class Sample {
#Value("${my.name}")
public static String name;
}
If I try Sample.name, it is always 'null'. So I tried this.
public class Sample {
public static String name;
#PostConstruct
public void init(){
name = privateName;
}
#Value("${my.name}")
private String privateName;
public String getPrivateName() {
return privateName;
}
public void setPrivateName(String privateName) {
this.privateName = privateName;
}
}
This code works. Sample.name is set properly. Is this good way or not? If not, is there something more good way? And how to do it?
First of all, public static non-final fields are evil. Spring does not allow injecting to such fields for a reason.
Your workaround is valid, you don't even need getter/setter, private field is enough. On the other hand try this:
#Value("${my.name}")
public void setPrivateName(String privateName) {
Sample.name = privateName;
}
(works with #Autowired/#Resource). But to give you some constructive advice: Create a second class with private field and getter instead of public static field.
Soruce of this info is this: https://www.baeldung.com/spring-inject-static-field
Spring uses dependency injection to populate the specific value when it finds the #Value annotation. However, instead of handing the value to the instance variable, it's handed to the implicit setter instead. This setter then handles the population of our NAME_STATIC value.
#RestController
//or if you want to declare some specific use of the properties file then use
//#Configuration
//#PropertySource({"classpath:application-${youeEnvironment}.properties"})
public class PropertyController {
#Value("${name}")//not necessary
private String name;//not necessary
private static String NAME_STATIC;
#Value("${name}")
public void setNameStatic(String name){
PropertyController.NAME_STATIC = name;
}
}
This is my sample code for load static variable
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class OnelinkConfig {
public static int MODULE_CODE;
public static int DEFAULT_PAGE;
public static int DEFAULT_SIZE;
#Autowired
public void loadOnelinkConfig(#Value("${onelink.config.exception.module.code}") int code,
#Value("${onelink.config.default.page}") int page, #Value("${onelink.config.default.size}") int size) {
MODULE_CODE = code;
DEFAULT_PAGE = page;
DEFAULT_SIZE = size;
}
}
For those who want to use ApplicationContext in the main class of a Spring Boot application, you can just use the return value of SpringApplication.run.
Although workarounds may need to be implemented, one should try to avoid them in most scenarios if possible. Spring is great at handling dependency injection and treats most objects as Singletons. This means that Spring can handle the creation of objects for you, and the injection of these objects at runtime. When combining this with the fact that your Spring managed bean is likely a Singleton, the use of static methods and variables is largely unnecessary. You can simply autowire in an instance of the object you are looking for at the constructor level or variable level and reference the non-static version of the method or variable. This is ideal and behaves similarly to a static reference. Non static variables are basically static because you are only ever using one instance of the object in every part of the code and because of dependency injection you are never handling the instantiation of the object, just like with a static reference! Great! Now I'm sure there are instances where you need the work around (i.e. you aren't using dependency injection or class is not a singleton), but try to not use workarounds if possible. Also this is just my 2 cents. Someone may be able to offer 3. (:
public class InjectableClass{
#Value("${my.value}")
private String myString;
public String nonStaticMethod(){
return myString;
}
}
public class LogicClass{
private InjectableClass injectableClass;
#Autowire
public LogicClass(InjectableClass injectableClass){
this.injectableClass = injectableClass;
}
public void logicClassMethod(){
System.out.println("Hey! Here is the value I set on myString: " +
injectableClass.nonStaticMethod() + ". That was
basically like using a static method!");
}
}

Resources