Friday, January 21, 2022

Spring Boot Application Common Configurations

Start Spring Boot and load properties


@SpringBootApplication(scanBasePackages = {"com.idd.abc.base"})
@EnableRetry
@EnableScheduling
@ConfigurationPropertiesScan({"com.idd.abc.base"})
@PropertySource(ignoreResourceNotFound = true, value = "file:/vault/secrets")
@Slf4j
public class Application {
  @SuppressWarnings("squid:S4823")
  public static void main(String[] args) {
    log.info("### ### ### ABC Spring Boot Base Application starting...");
    SpringApplication app =
        new SpringApplication(Application.class, AbcMetricsServiceCounterConfiguration.class);
    app.setApplicationStartup(new BufferingApplicationStartup(2048));
    app.run(args);
  }
  @Bean
  public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
    PropertySourcesPlaceholderConfigurer propsConfig = new PropertySourcesPlaceholderConfigurer();
    propsConfig.setLocation(new ClassPathResource("git.properties"));
    propsConfig.setIgnoreResourceNotFound(true);
    propsConfig.setIgnoreUnresolvablePlaceholders(true);
    return propsConfig;
  }
}

Spring Boot Application Statuses


@Component
@RequiredArgsConstructor
@Slf4j
public class AbcApplicationGracefulShutdown
    implements ApplicationListener<ContextClosedEvent>, ExitCodeGenerator {
  @Override
  public void onApplicationEvent(ContextClosedEvent event) {
    log.info("@@@ @@@ @@@ ABC Application is closed by event:{}.", event.getSource());
  }
  @Override
  public int getExitCode() {
    return 0;
  }
}

@Component
@Order(0)
@Slf4j
class AbcApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
  @Value("${spring.profiles.active}")
  private String activeProfiles;
  @Override
  public void onApplicationEvent(ApplicationReadyEvent event) {
    log.info(
        "@@@ @@@ @@@ ABC Application is ready and started with profile:{}.", activeProfiles);
  }
}

Async Configuraiton


@Configuration
@RequiredArgsConstructor
@EnableAsync
public class AbcAsyncConfigurer implements AsyncConfigurer {
  @SuppressWarnings("squid:S3749")
  private final AbcAsyncExceptionHandler asyncExceptionHandler;
  @Value("${async.handler.concurrency:3}")
  private int asyncCorePoolSize;
  @Value("${async.queue.capacity:3500}")
  private int asyncQueueCapacity;
  @Bean
  public Executor asyncTaskExecutor() {
    final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(asyncCorePoolSize);
    executor.setMaxPoolSize(3 * asyncCorePoolSize);
    executor.setQueueCapacity(asyncQueueCapacity);
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.setThreadNamePrefix("AsyncTaskExecutore-");
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.initialize();
    return executor;
  }

  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return asyncExceptionHandler;
  }
}

@Service
@Slf4j
public class AbcAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
  @Override
  public void handleUncaughtException(final Throwable t, final Method m, final Object... params) {
    log.info("$$$ $$$ $$$ Method name:{}", m.getName());
    for (Object param : params) {
      log.info("$$$ $$$ $$$ Parameter value:{}", param);
    }
    if (t instanceof RetryExhaustedException) {
      log.error(
          AbcAlertMessage.ABC_APP_RETRY_EXHAUSTED
              + " *** *** *** method:"
              + m
              + " failed with recoverable exception and all retries exhausted. Message:"
              + t.getMessage(),
          t);
    } else if (t instanceof AbcApplicationException) {
      log.error(
          AbcAlertMessage.ABC_APP_UNRECOVERABLE_FAILURE
              + " *** *** ***  method:"
              + m
              + " falied with Un-recoverable Exception. Message:"
              + t.getMessage(),
          t);
    } else {
      log.error(
          AbcAlertMessage.ABC_APP_UN_CATEGORIZED_EXCEPTION
              + " *** *** ***  method:"
              + m
              + " failed with Un-categoried Exception. Message:"
              + t.getMessage(),
          t);
    }
  }
}

Cache


@Configuration
@EnableCaching
public class AbcCacheManagerConfiguration {
  @Bean
  @Primary
  public CacheManager abcDefaultCacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager("abcDefaultCache");
    cacheManager.setCaffeine(
        Caffeine.newBuilder()
            .initialCapacity(10)
            .maximumSize(200)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .weakKeys()
            .recordStats());
    return cacheManager;
  }

  @Bean
  public CacheManager abcSimpleCacheManager() {
    return new ConcurrentMapCacheManager("abcSimpleCache");
  }
}

@Configuration
@Slf4j
public class AbcBaseConfiguration {
  @Bean
  public SecureRandom random() {
    return new SecureRandom();
  }

  @Bean
  public String hostnameProperty(final Environment env) {
    String hostname = env.getProperty("HOSTNAME");
    if (null == hostname) {
      try {
        hostname = InetAddress.getLocalHost().getHostName();
      } catch (UnknownHostException e) {
        log.warn(
            AbcAlertMessage.ABC_APP_INTERNAL_EXCEPTION
                + " ### ### ### Failed to retrieve hostname.",
            e);
      }
    }
    return hostname;
  }

  @Bean
  public Clock clock() {
    return Clock.system(ZoneId.of("UTC"));
  }

  @Bean
  public InetAddress inetAddress() {
    try {
      return InetAddress.getLocalHost();
    } catch (Exception ex) {
      throw new AbcApplicationException(
          AbcAlertMessage.ABC_APP_INTERNAL_EXCEPTION
              + " ### ### ### Reading local inet address failed. Message:"
              + ex.getMessage(),
          ex);
    }
  }

  @Bean
  MeterRegistryCustomizer<MeterRegistry> configurer(
      @Value("${spring.application.name}") String applicationName) {
    return registry -> registry.config().commonTags("application", applicationName);
  }
}

@Configuration
public class AbcMetricsServiceCounterConfiguration {
  private static final String CAT = "cat";
  private static final String SERVICE = "service";
  @Bean
  public Counter userErrorCounter(MeterRegistry meterRegistry) {
    return meterRegistry.counter("user.id.error", CAT, SERVICE);
  }
  @Bean
  public Counter userAccessCounter(MeterRegistry meterRegistry) {
    return meterRegistry.counter("user.id.access", CAT, SERVICE);
  }
}

@Configuration
public class TimedAspectConfiguration {
  @Bean
  public TimedAspect timedAspect(MeterRegistry meterRegistry) {
    return new TimedAspect(meterRegistry);
  }
}


===================================================================
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Service
@Validated
public @interface ValidatedService {}

Condition

public class SolaceCondition extends AnyNestedCondition {

  SolaceCondition() {
    super(ConfigurationPhase.PARSE_CONFIGURATION);
  }

  @ConditionalOnProperty(
      value = "solace.jms.direct.enabled",
      havingValue = "true",
      matchIfMissing = false)
  static class JmsDirectCondition {}

  @ConditionalOnProperty(
      value = "solace.jms.spring.enabled",
      havingValue = "true",
      matchIfMissing = false)
  static class JmsSpringCondition {}
}

public class OnUnixCondition implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    return SystemUtils.IS_OS_LINUX;
  }
}

Usage Demo

@ValidatedService
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
  @SuppressWarnings("squid:S3749")
  private final SecureRandom random;

  @SuppressWarnings("squid:S3749")
  @Qualifier("userAccessCounter")
  private final Counter accessCouter;

  private SoftReference<Map<Long, User>> userMapRef = new SoftReference<>(new HashMap<>());

  @Timed(
      extraTags = {"cat", "service"},
      percentiles = {0.50, 0.95, 0.99},
      histogram = false)
  @Log4jDiagnosticContextEnable
  @Override
  public String getAccount(@NotNull final User user) {
    accessCouter.increment();
    return user.getId() + "-" + random.nextInt(100000000);
  }

  /*
   * Example of asynchronous execution
   */
  @Async("asyncTaskExecutor")
  @Timed(
      extraTags = {"cat", "service"},
      percentiles = {0.50, 0.95, 0.99},
      histogram = false)
  @Log4jDiagnosticContextEnable
  @Override
  public ListenableFuture<String> getIdAndName(@NotNull final User user) {
    accessCouter.increment();
    return new AsyncResult<>(user.getId() + "-" + user.getName());
  }

  /*
   * Example of handling AsyncUncaughtExceptionHandler
   */
  @Async("asyncTaskExecutor")
  @Timed(
      extraTags = {"cat", "service"},
      percentiles = {0.50, 0.95, 0.99},
      histogram = false)
  @Log4jDiagnosticContextEnable
  @Override
  public void asyncFailure(final User user) {
    accessCouter.increment();
    throw new RuntimeException("TEST EXCEPTION");
  }

  @Timed(
      extraTags = {"cat", "service"},
      percentiles = {0.50, 0.95, 0.99},
      histogram = false)
  @Log4jDiagnosticContextEnable
  @Override
  public synchronized User getUser(long id) {
    Map<Long, User> userMap = this.getUserMap();
    User user = userMap.get(id);
    if (user == null) {
      user = createUser(id);
    }

    return user;
  }

  @Timed(
      extraTags = {"cat", "service"},
      percentiles = {0.50, 0.95, 0.99},
      histogram = false)
  @Log4jDiagnosticContextEnable
  @Override
  public synchronized User updateUser(long id) {
    Map<Long, User> userMap = this.getUserMap();
    User user = userMap.get(id);
    if (user == null) {
      user = createUser(id);
    } else {
      user.setName(String.valueOf(random.nextInt(100)));
    }

    return user;
  }

  private Map<Long, User> getUserMap() {
    Map<Long, User> userMap = this.userMapRef.get();
    if (userMap == null) {
      this.userMapRef = new SoftReference<>(new ConcurrentHashMap<>());
    }
    return userMapRef.get();
  }

  private User createUser(long id) {
    User user;
    user = new User();
    user.setId(id);
    user.setName(String.valueOf(random.nextInt(100)));
    return user;
  }
}



No comments:

Post a Comment