31 Ağustos 2021 Salı

SpringJMS Kullanımı

Giriş
Producer ve Consumer arasındaki kullanılacak mesaj formatını belirlenir. 
- JSON seçilebilir.
Bu durumda Producer şöyledir
Gson gson = new Gson();
String jsonPerson = gson.toJson(person);

jmsTemplate.convertAndSend(destinationTopic, jsonPerson);
Bu durumda Consumer şöyledir
@JmsListener(...)
public void listen(String mensagem) {
  Person person = new Person();
  try {
    Gson gson = new Gson();
    person = gson.fromJson(mensagem, Person.class);
    ...
  }catch(Exception e){
    ...
  }
}

Producer
1. @EnableJms tanımlanır
2. ActiveMQConnectionFactory bean yaratılır
2. JmsTemplate bean yaratılır

Consumer
1. @EnableJms tanımlanır
2. ActiveMQConnectionFactory bean yaratılır
3. JmsListenerContainerFactory  bean yaratılır. Bu arayüzü gerçekleştiren sınıf DefaultJmsListenerContainerFactory 
4. @JmsListener ile mesajları işleyecek kod belirtilir.


30 Ağustos 2021 Pazartesi

OpenAPI Kullanımı

OpenAPI Nedir
Açıklaması şöyle
The OpenAPI specification defines how to write HTTP APIs that can be consumed by any programming language and provide insight into the APIs’ functionality without access to source code or documentation. In other words, following the specification makes it easier for consumers to understand what it does and how to use it. Tools, such as Swagger, can then be used to display documentation without developers maintaining documentation separate from an API’s code.

All these described points translate into happier users while mitigating some of the burdens you’ll face while supporting your APIs.
Openapi-processor
İlk defa burada gördüm. Maven veya Gradle kullanarak YAML dosyasından Spring sınıflar üretebiliyor

OpenAPI vs Swagger
Açıklaması şöyle
Evolution from Swagger to OpenAPI
Swagger was initially released in 2011 as a specification and a large ecosystem for building API-related tools (documentation, client SDK generation, etc.). In 2015, the Swagger Specification was donated to the OpenAPI Initiative, becoming the foundation of the OpenAPI Specification (OAS). This marked the evolution of Swagger to OpenAPI, although the term “Swagger” is still commonly used when referring to the tools and ecosystem, whereas “OpenAPI” refers to the specification itself.
Açıklaması şöyle
Although the terms once referred to the same thing, they can no longer be used interchangeably…even though some people still do, in 2021 OpenAPI refers to the industry-standard specification for RESTful API design. Swagger refers to a set of SmartBear tools.
OpenAPI'yi gerçekleştiren kütüphaneler şeklen şöyle


Springfox Gerçekleştirimi
Swagger (OpenAPI ) Kullanımı yazısına taşıdım

SpringDoc Gerçekleştirimi
SpringDoc OpenAPI Kullanımı yazısına taşıdım

SpringBoot Actuator shutdown Endpoint

Giriş
Açıklaması şöyle.. Uygulamayı sonlandırır. Bu endpoint normalde etkin değildir, etkinleştirmek gerekir.
Shutdown is an endpoint that allows the application to be gracefully shutdown. This feature is not enabled by default. You can enable this by using management.endpoint.shutdown.enabled=true in your application.properties file. But be careful about this if you are using this.
shutdown için 2 tane yöntem var. Açıklaması şöyle.
There are two supported values for this property
- immediate: this is the default value and will cause the server to shut down immediately.
- graceful: this will enable the graceful shutdown and start respecting the timeout given in next property.

spring.lifecycle.timeout-per-shutdown-phase : This takes the values in java.time.Duration format. which will be allowed before the application is shutdown.
1.immediate Yöntem
Hemen kapatılır

2. graceful Yöntem
Açıklaması şöyle
Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and reactive and Servlet-based web applications.
Şöyle yaparız. Burada varsayılan bekleme süresi 30 saniye.
management.endpoint.shutdown.enabled=true
server.shutdown=graceful
Açıklaması şöyle. Mevcut istek yoksa yeni istek kabul edilmez ve sunucu kapanır, ancak mevcut istek varsa belirli bir süre bu isteğin işlenip bitmesini bekler.
Now as we have graceful shutdown configured, there could be two possibilities:

- There are no inflight requests. In this case the application will proceed to shutdown without waiting for grace period to complete.
- If there are any requests in flight, then application will wait for the grace period and shutdown. If there are still requests pending even after grace period, the application will throw an exception and proceed to force shutdown with below logs.
Not : graceful yöntem özellikle verinin bozulmaması (data corruption) için önemlidir.

2.1 Bekleme Süresi Tanımlamak
Varsayılan bekleme süresi 30 saniye. Ancak istenirse değiştirilebilir. Açıklaması şöyle.
During a graceful shutdown Spring Boot allows some grace period to the application to finish all the current requests or processes. Once, the grace period is over the unfinished processes or requests are just killed. By default, Spring Boot allows a 30 seconds graceful shutdown timeout. However, we can configure it by using application properties or yaml file.
Örnek
Şöyle yaparız
server:
  shutdown: "graceful"

spring:
  lifecycle:
    timeout-per-shutdown-phase: "45s" # Default is 30s
Örnek
20 saniye vermek için şöyle yaparız
# Enable graceful shutdown
server.shutdown=graceful
# Allow grace timeout period for 20 seconds
spring.lifecycle.timeout-per-shutdown-phase=20s


# Force enable health probes. Would be enabled on kubernetes platform by default
management.health.probes.enabled=true
Graceful shutdown ile loglar şuna benzer. Burada shutdown isteği alındıktan sonra timeout-per-shutdown-phase süresi kadar daha mevcut isteklerin bitirilmeye çalışıldığı görülebilir, çünkü JMSListener çalışmaya devam ediyor.
14:27:41|INFO | o.s.b.w.e.t.GracefulShutdown:53 - Commencing graceful shutdown. Waiting for active requests to complete
14:27:41|INFO | o.s.b.w.e.t.GracefulShutdown:78 - Graceful shutdown complete
14:27:49|INFO | c.a.s.t.s.w.Listener:19 - Finished processing JMS Message
14:27:49|INFO | o.s.s.c.ThreadPoolTaskExecutor:218 - Shutting down ExecutorService 'applicationTaskExecutor'
14:27:49|INFO | c.z.h.HikariDataSource:350 - HikariPool-1 - Shutdown initiated...
14:27:49|INFO | c.z.h.HikariDataSource:352 - HikariPool-1 - Shutdown completed.
3. Dışarıdan Tetiklemek
Örnek

shutdown endpoint'i tetiklemek için şöyle yaparız. Rest noktası olduğu için JSON post ediliyor.
String url = "http://localhost:8080/actuator/shutdown";

HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON),

RestTemplate restTemplate = new RestTemplate();

HttpEntity<String> request = new HttpEntity("",httpHeaders);
ResponseEntity<String> response = restTemplate.postForEntity(url,request,String.class);

SpringData JPA @QueryHints Anotasyonu

Giriş
Şu satırı dahil ederiz
import org.springframework.data.jpa.repository.QueryHints;
1. Bu anotasyon ile Hibernate Query Cache tanımlanabilir.

2. Bu anotasyon ile Lock yöntemi tanımlanabilir. 
Açıklaması şöyle
When using Spring Data, we can use the @Lock annotation in Spring repository methods to specify the desired lock mode. The lock timeout value can be defined by setting the javax.persistence.lock.timeout hint in a @QueryHints annotation applied to a Spring repository method.


SpringData JPA @Lock Anotasyonu - SELECT FOR UPDATE vs İçindir

Giriş
Şu satırı dahil ederiz
import org.springframework.data.jpa.repository.Lock;
Açıklaması şöyle
When using Spring Data, we can use the @Lock annotation in Spring repository methods to specify the desired lock mode.
LockModeType  olarak 
1. PESSIMISTIC_READ, 
2. PESSIMISTIC_WRITE, 
3. PESSIMISTIC_FORCE_INCREMENT
4. OPTIMISTIC_FORCE_INCREMENT 
kullanılabilir

Aslında altta şöyle bir şeye denk geliyor.
entityManager.lock(employee, LockModeType.PESSIMISTIC_WRITE);
Timeout 
Açıklaması şöyle
To lock entities pessimistically, set the lock mode to PESSIMISTIC_READ, PESSIMISTIC_WRITE, or PESSIMISTIC_FORCE_INCREMENT.

If a pessimistic lock cannot be obtained, but the locking failure doesn’t result in a transaction rollback, a LockTimeoutException is thrown.

Pessimistic Locking Timeouts

The length of time in milliseconds the persistence provider should wait to obtain a lock on the database tables may be specified using the javax.persistence.lock.timeout property. If the time it takes to obtain a lock exceeds the value of this property, a LockTimeoutException will be thrown, but the current transaction will not be marked for rollback. If this property is set to 0, the persistence provider should throw a LockTimeoutException if it cannot immediately obtain a lock.

If javax.persistence.lock.timeout is set in multiple places, the value will be determined in the following order:

1. The argument to one of the EntityManager or Query methods.
2. The setting in the @NamedQuery annotation.
3. The argument to the Persistence.createEntityManagerFactory method.
4. The value in the persistence.xml deployment descriptor.
LockModeType = PESSIMISTIC_READ
Sanırım şöyle bir SQL üretiyor
SELECT * FROM t WHERE i = 1 FOR SHARE;
LockModeType = PESSIMISTIC_WRITE
Örnek
Şöyle bir SQL üretiyor.
'select id from table where id = ? for update wait 5'
Şöyle yaparız.
interface WidgetRepository extends Repository<Widget, Long> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  Widget findOne(Long id);
}
LockModeType = OPTIMISTIC_FORCE_INCREMENT 
Örnek
Elimizde şöyle bir kod olsun
@Entity
public class Product {

   @Id
   private Long id;

   private String name;

   private int quantity;

   @Version
   private Long version;

   // Getters and setters
}

@Repository
public interface ProductRepository extends 
  JpaRepository<Product, Long> {

   @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
   Optional<Product> findById(Long id);

}
Kaydederken saveAll() için şöyle yaparız. Böylece bir sürü satır tek bir SQL cümlesi ile kaydedilir.
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
Kullanmak için şöyle yaparız createProduct() işleminde flush() çağrılıyor. Böylece aynı versiyon numarasına sahip iki tane istek gelse bile flush() sayesinde hata varsa hemen görebiliriz
@Service
public class ProductService {
    
  @Autowired
  private ProductRepository productRepository;

  @Transactional
  public void updateProducts(List<Product> products) {
    productRepository.saveAll(products);
  }

  @Transactional
  public void createProduct(Product product) {
    productRepository.save(product);
    productRepository.flush(); // commit the transaction
  }
}


28 Ağustos 2021 Cumartesi

SpringTest Testcontainers @TestContainers Anotasyonu

(disabledWithoutDocker Alanı
Örnek
Şöyle yaparız
@SpringBootTest
@TestContainers(disabledWithoutDocker = true)
@ContextConfiguration(initializers = ExampleIntegrationTests.Initializer.class)
class ExampleIntegrationTests {
  @Container
  public static CassandraContainer<?> cassandra = new CassandraContainer<>();
  
  static class Initializer 
    implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext context) {
      int cqlPort = cassandra.getMappedPort(CassandraContainer.CQL_PORT);
      TestPropertyValues.of("spring.data.cassandra.port="+ cqlPort)
        .applyTo(context.getEnvironment());
    }
  }
}

27 Ağustos 2021 Cuma

SpringWebFlux Mono.then metodu

Giriş
Metodumuz parametre olarak Mono<Foo> alsın ancak Mono<Void> dönsün. Bu durumda Mono.then() kullanılır. Benzer bir kullanım Flux.then() olarak ta var

Örnek
Şöyle yaparız
@PostMapping("file/single")
public Mono<Void> upload(@RequestPart("user-name") String name,
                         @RequestPart("fileToUpload") Mono<FilePart> filePartMono){
  return filePartMono
    .doOnNext(fp -> System.out.println("Received File : " + fp.filename()))
    .flatMap(fp -> fp.transferTo(basePath.resolve(fp.filename())))
    .then();
}

SpringWebFlux FilePart Arayüzü

Giriş
Şu satırı dahil ederiz. WebFlux ile MultiPartFile kullanılamıyor
import org.springframework.http.codec.multipart.FilePart;
transferTo metodu
Örnek
Şöyle yaparız
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;

@RestController
@RequestMapping("upload")
public class UploadController {

  private final Path basePath = Paths.get("./src/main/resources/upload/");

  @PostMapping("file/single")
  public Mono<Void> upload(@RequestPart("user-name") String name,
                           @RequestPart("fileToUpload") Mono<FilePart> filePartMono){
    System.out.println("user : " + name);
    return filePartMono
      .doOnNext(fp -> System.out.println("Received File : " + fp.filename()))
      .flatMap(fp -> fp.transferTo(basePath.resolve(fp.filename())))
      .then();
  }

  @PostMapping("file/multi1")
  public Mono<Void> upload(@RequestPart("files") Flux<FilePart> partFlux){
    return partFlux
      .doOnNext(fp -> System.out.println(fp.filename()))
      .flatMap(fp -> fp.transferTo(basePath.resolve(fp.filename())))
      .then();
  }
}

26 Ağustos 2021 Perşembe

SpringBoot Test Sliced Test Anotasyonları

Test Slices Nedir?
Açıklaması şöyle. @SpringBootTest kullanmaktan çok daha iyi
Whenever your tests go beyond trivial unit testing and you include Spring Test features, you'll most likely start a customized Spring Context (e.g. @SpringBootTest@WebMvcTest@DataJpaTest).
Açıklaması şöyle.
Test Slices are a Spring Boot feature introduced in the 1.4. The idea is fairly simple, Spring will create a reduced application context for a specific slice of your app.

Also, the framework will take care of configuring the very minimum.

There are a sensible number of slices available out of the box in Spring Boot and we can create our own too:

- @JsonTest: Registers JSON relevant components
- @DataJpaTest: Registers JPA beans, including the ORM available
- @JdbcTest: Useful for raw JDBC tests, takes care of the data source and in memory DBs without ORM frills
- @DataMongoTest: Tries to provide an in-memory mongo testing setup
- @WebMvcTest: A mock MVC testing slice without the rest of the app

This particular feature if used wisely can help us build narrow tests without such a big penalty in terms of performance particularly for small/medium sized apps.
Bazı anotasyonlar şöyle
@DataLdapTest
@DataMongoTest
@DataNeo4jTest
@DataRedisTest

@JsonTest
@JooqTest


@RestClientTest
@WebFluxTest

SpringData Flyway Sınıfı

Giriş
Şu satırı dahil ederizz
import org.flywaydb.core.Flyway;
Eğer bu sınıfı bir bean olarak kullanacaksak şöyle yaparız. Flyway her zaman JPA 1EntityManagerFactory sınıfından önce çalışmalıdır
@Bean
@DependsOn("flyway")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    ...
}
callbacks metodu
Örnek
Şöyle yaparız
Flyway flyway = Flyway.configure()
  .dataSource(dataSource)
  .locations("db/migration", "db/callbacks")
  .callbacks(new ExampleFlywayCallback())
  .load();
flyway.migrate();
locations metodu
Örnek
Şöyle yaparız. Burada scriptler "src/main/resources/flyway/scripts/postgresql/migration" gibi bir dizindedir
@Bean
public Flyway flyway() {
    Flyway flyway = Flyway.configure()
        .dataSource(dataSource())
        .baselineOnMigrate(true)
        .locations(
            String.format(
                "classpath:/flyway/scripts/postgresql/migration",
                databaseType.name().toLowerCase()
            )
    ).load();
    flyway.migrate();
    return flyway;
}
migrate metodu
Örnek
Şöyle yaparız
public class TestClass {
  @Autowired Flyway flyway;
  @BeforeEach
  public void beforeEach() {
    flyway.clean();
    flyway.migrate();
  }
  @Test
  public void example1() {
    // test example
  }
}

25 Ağustos 2021 Çarşamba

SpringSecurity AbstractAuthenticationToken Sınıfı

Giriş
Şu satırı dahil ederiz
import org.springframework.security.authentication.AbstractAuthencticationToken;
Tüm AuthenticationToken sınıflarının atasıdır. Authentication arayüzünden kalıtır. Açıklaması şöyle
It is a base class for Authentication objects and creates a token with the supplied array of authorities (GrantedAuthority).

It implements Authentication interface and it has a collection of “authorities” you can set for your authenticated account and a “details” object where you can set other info you may use later (like an auth token to set to response header or save in a cache (Redis, etc.) in your custom success handler class).
gibi sınıflara bakılabilir

getCredential metodu
Bir örnek burada

getPrincipal metodu
Bir örnek burada

24 Ağustos 2021 Salı

SpringQuartz Kullanımı Trigger Yaratma

Giriş
Açıklaması şöyle. Yani Trigger'a bir isim ve grup veriliyor
Trigger — A component that determines the schedule upon which a given Job will be performed. Every Trigger is identified by the TriggerKey that has a name and group and the name must be unique within a group. Triggers can also send data to the Job
TriggerBuilder: A builder pattern implementation to create a Trigger instance
Trigger Çeşitleri
Açıklaması şöyle.
There are 2 types of triggers,
Simple Trigger — Used if you need to execute a job exactly once at a specific moment in time, or at a specific moment in time followed by repeats at a specific interval.
Cron Trigger — For scheduling jobs having complex times like if you need to execute a job that recurs based on calendar-like notions
org.quartz.Trigger yaratmak için kendi kodumuzda şu yöntemlerden birisini kullanırız

1. Quartz projesine ait TriggerBuilder kullanılır. 
2. Spring projesine ait SimpleTriggerFactoryBean kullanılır
3. Spring projesine ait CronTriggerFactoryBean kullanılır

TriggerBuilder 
forJob metodu
Örnek
org.quartz.Trigger yaratmak için kendi kodumuzda Quartz projesine ait TriggerBuilder kullanılır. Şöyle yaparız
private Trigger buildJobTrigger(JobDetail jobDetail, ZonedDateTime startAt) {
  return TriggerBuilder.newTrigger()
    .forJob(jobDetail)
    .withIdentity(jobDetail.getKey().getName(), "email-triggers")
    .withDescription("Send Email Trigger")
    .startAt(Date.from(startAt.toInstant()))
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow())
    .build();
}
Spring SimpleTriggerFactoryBean
İşi belirtilen zamanda koşan basit bir SimpleTrigger nesnesi yaratır
Örnek
Şöyle yaparız
Trigger buildTrigger() {
  SimpleTriggerFactoryBean triggerFactory = new SimpleTriggerFactoryBean();

  triggerFactory.setName("SimpleTriggerName");
  triggerFactory.setGroup("Group");
  triggerFactory.setStartTime(localTimeToDate(LocalTime.now().plusHours(2)));
  triggerFactory.setRepeatCount(0);
  triggerFactory.setRepeatInterval(0);

  triggerFactory.afterPropertiesSet();
  return triggerFactory.getObject();
}
Şöyle yaparız
/**
 * Create simple trigger.
 *
 * @param triggerName        Trigger name.
 * @param startTime          Trigger start time.
 * @param repeatTime         Job repeat period mills
 * @param misFireInstruction Misfire instruction (what to do in case of misfire happens).
 * @return {@link SimpleTrigger}
*/
public SimpleTrigger createSimpleTrigger(String triggerName, Date startTime,
Long repeatTime, int misFireInstruction) {
  SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
  factoryBean.setName(triggerName);
  factoryBean.setStartTime(startTime);
  factoryBean.setRepeatInterval(repeatTime);
  factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
  factoryBean.setMisfireInstruction(misFireInstruction);
  factoryBean.afterPropertiesSet();
  return factoryBean.getObject();
}
Spring CronTriggerFactoryBean Sınıfı
Cron gibi çalışan daha karmaşık iş zamanlama yeteneği sağlar. 
Örnek
Şöyle yaparız
Trigger buildCronTrigger() {
  CronTriggerFactoryBean crontriggerFactory = new CronTriggerFactoryBean();
  crontriggerFactory.setName("CronName");
  crontriggerFactory.setGroup("Group");
  crontriggerFactory.setCronExpression("2 * * * * *");
  crontriggerFactory.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);

  try {
    crontriggerFactory.afterPropertiesSet();
   } catch (ParseException e) {
     ...
  }
  return crontriggerFactory.getObject();
}

Örnek
Şöyle yaparız
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.SimpleTrigger;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
   
/**
 * Create cron trigger.
 *
 * @param triggerName        Trigger name.
 * @param startTime          Trigger start time.
 * @param cronExpression     Cron expression.
 * @param misFireInstruction Misfire instruction (what to do in case of misfire happens).
 * @return {@link CronTrigger}
*/
public CronTrigger createCronTrigger(String triggerName, Date startTime,
String cronExpression, int misFireInstruction) {
  CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
  factoryBean.setName(triggerName);
  factoryBean.setStartTime(startTime);
  factoryBean.setCronExpression(cronExpression);
  factoryBean.setMisfireInstruction(misFireInstruction);
  try {
    factoryBean.afterPropertiesSet();
  } catch (ParseException e) {
    log.error(e.getMessage(), e);
  }
  return factoryBean.getObject();
}

SpringQuartz Kullanımı JobDetail Yaratma

JobBuilder Sınıfı
org.quartz.JobDetail  yaratmak için kendi kodumuzda Quartz projesine ait JobBuilder kullanılır.

newJob metodu
Burada Job sınıfının sınıf tipini vermek gerekir.
Job iki şekilde kodlanabilir
1. Quartz projesinin Job arayüzünden kalıtılır
2. Spring' ait QuartzJobBean sınıfından  kalıtılır

Örnek
Elimizde şöyle bir Job olsun
public class FileDeletionJob implements Job {
  ...
}
Şöyle yaparız. Burada JobBuilder ile bir JobDetail yaratılıyor. Daha sonra Trigger yaratılıyor. Scheduler.schedule() çağrısına JobDetail ve Trigger geçiliyor.
public static void configureJob(String path, String regex) throws SchedulerException {
  JobDetail job = JobBuilder.newJob(FileDeletionJob.class)
    .usingJobData("regex", regex)
    .usingJobData("path", path)
    .build();

  Trigger trigger = newTrigger().withSchedule(simpleSchedule()
                                .withIntervalInMinutes(1)
     // .withIntervalInHours(1)
     .repeatForever())
     .build();

  Scheduler scheduler = new StdSchedulerFactory().getScheduler();
  scheduler.start();
  scheduler.scheduleJob(job, trigger);
}
usingJobData metodu
Örnek
Şöyle yaparız
JobDetail buildJobDetail(ScheduleReportRequest scheduleReportRequest) {
  JobDataMap jobDataMap = new JobDataMap();

  jobDataMap.put("reportname", scheduleEmailRequest.getEmail());
  jobDataMap.put("type", scheduleEmailRequest.getSubject());
       

  return JobBuilder.newJob(ReportJob.class)
    .withIdentity(UUID.randomUUID().toString(), "report-jobs")
    .withDescription("Send Reports")
    .usingJobData(jobDataMap)
    .storeDurably()
    .build();
}

storeDurably metodu
Örnek
Şöyle yaparız
private JobDetail buildJobDetail(ScheduleEmailRequest scheduleEmailRequest) {
  JobDataMap jobDataMap = new JobDataMap();
  jobDataMap.put("email", scheduleEmailRequest.getEmail());
  jobDataMap.put("subject", scheduleEmailRequest.getSubject());
  jobDataMap.put("body", scheduleEmailRequest.getBody());
  return JobBuilder.newJob(EmailJob.class)
    .withIdentity(UUID.randomUUID().toString(), "email-jobs")
    .withDescription("Send Email Job")
    .usingJobData(jobDataMap)
    .storeDurably()
    .build();
}
Spring JobDetailFactoryBean 
org.quartz.JobDetail  yaratmak için kendi kodumuzda Spring projesine ait JobDetailFactoryBean kullanırız

Örnek
Şöyle yaparız
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * Create Quartz Job.
 *
 * @param jobClass  Class whose executeInternal() method needs to be called.
 * @param isDurable Job needs to be persisted even after completion. if true, job will be
persisted, not otherwise.
 * @param context   Spring application context.
 * @param jobName   Job name.
 * @param jobGroup  Job group.
 * @return JobDetail object
*/
public JobDetail createJob(Class<? extends QuartzJobBean> jobClass, boolean isDurable,
                           ApplicationContext context, String jobName, String jobGroup) {
  JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
  factoryBean.setJobClass(jobClass);
  factoryBean.setDurability(isDurable);
  factoryBean.setApplicationContext(context);
  factoryBean.setName(jobName);
  factoryBean.setGroup(jobGroup);

  // set job data map
  JobDataMap jobDataMap = new JobDataMap();
  jobDataMap.put(jobName + jobGroup, jobClass.getName());
  factoryBean.setJobDataMap(jobDataMap);

  factoryBean.afterPropertiesSet();

  return factoryBean.getObject();
}

23 Ağustos 2021 Pazartesi

SpringCloud Config Server Kullanımı

Giriş
Açıklaması şöyle
With the Spring Cloud Configuration server, we can place the configuration files for all our microservices in a central configuration repository that will make it much easier to handle them. Our microservices will be updated to retrieve their configuration from the configuration server at startup.
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-server</artifactId>
</dependency>
JDBC için şu satırı dahil ederiz
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
Config Repository Olarak Ne Kullanılabilir
Açıklaması şöyle. Yani repository olarak bir sürü seçenek var
The config server supports the storing of configuration files in a number of different backends, for example:  

Git repository  
Local filesystem  
HashiCorp Vault  
A JDBC database  

Other Spring projects have added extra backends for storing configuration, for example, the Spring Cloud AWS project, which has support for using either AWS Parameter Store or AWS Secrets Manager as backends. 
Vault için bir örnek burada

Deciding on the Initial Client Connection  
Açıklaması şöyle
By default, a client connects first to the config server to retrieve its configuration....
It is also possible to do this the other way around, that is, the client first connecting to the discovery server to find a config server instance and then connecting to the config server to get its configuration. There are pros and cons to both approaches.  

Note: One concern with connecting to the config server first is that the config server can become a single point of failure. If the clients connect first to a discovery server, such as Netflix Eureka, there can be multiple config server instances registered so that a single point of failure can be avoided.
Config Server API
Açıklaması şöyle
The config server exposes a REST API that can be used by its clients to retrieve their configuration. In this chapter, we will use the following endpoints in the API:  

1. /actuator: The standard actuator endpoint is exposed by all microservices. 
As always, these should be used with care. They are very useful during development but must be locked down before being used in production.  

2. /encrypt and /decrypt: Endpoints for encrypting and decrypting sensitive information. These must also be locked down before being used in production.  

3. /{microservice}/{profile}: Returns the configuration for the specified microservice and the specified Spring profile.
actuator/refresh microservice'leri yeni konfigürasyondan haberdar etmek için kullanılır. Şeklen şöyle


/encrypt ve /decrypt
Örnek - symmetric
Şöyle yaparız
server.port = 8888

# Set path to Local Git Repository
spring.cloud.config.server.git.uri = /home/...

# Setting the key for Symmetric Encryption and Decryption
encrypt.key = secret
Böylece artık 
localhost:888/encrypt
adresine POST yaparsak gönderdiğimiz veriyi şifreli olarak gelir alırız. Eğer şifreli veriyi açmak istersek
localhost:888/encrypt
adresine yine POST yapmak gerekir.
Örnek - asymmetric
keytool komutu ile bir public/private key JKS oluşturulur. Şöyle yaparız
keytool -genkeypair \
  -keyalg RSA \
  -dname "cn=Config-Serv, ou=Java, o=Spring, c=US" \
  -alias configserv \
  -keypass nopass \
  -keystore simple.jks \
  -storepass nopass
Şöyle yaparız
# Path of Key-Pairs to be used. (Recommended: Use Classpath)
encrypt.keyStore.location = claspath:/simple.jks

# Represents the password to read jks file (keypass)
encrypt.keyStore.password = nopass

# Represents the password to read jks file (storepass)
# From Java-11 keystore password and secret should be same.
# Optional to specify. When not given keystore password is used as secret by default.
encrypt.keyStore.secret = nopass

# Value of Alias used in jks generation
encrypt.keyStore.alias = configserv

# Format of keystore (Default: jks)
encrypt.keyStore.type = jks
Kullanım
1. @EnableConfigServer anotasyonu tanımlanır

2. Sunucu için SpringCloud Config Server application.properties ayarları tanımlanır

3. İstemci için ayarlar yapılır. Ayarlarda application + profile + label bilgilerinin belirtilmesi gerekiyor. Yani şöyledir
application-name
|--- application-name-dev.properties
|--- application-name-qa.properties