17 Haziran 2019 Pazartesi

Circular Dependency

Giriş
Hata şöyle
Requested bean is currently in creation: Is there an unresolvable circular reference?
Çözümler
Çözümler şöyle
1. Refactoring ile iki sınıfa bir ortak arayüz eklemek
2. Bir sınıfı @Lazy kullanır hale getirmek
3. Field Injection kullanmak. Bu çözümde @Lazy kullanmak zorunda değiliz.
4. Setter Injection kullanmak. Bu çözümde @Lazy kullanmak zorunda değiliz.
3. @PostConstruct anotasyonu kullanmak

1. Refactoring 
Örnek
Elimizde şöyle bir kod olsun. Bu kod Circular Dependency olduğu için çalışmaz
@Service
public class Order {
  private Customer customer;

  public Order(Customer customer) {
    this.customer = customer;
  }
}

@Service
public class Customer {
  private Order order;

  public Customer(Order order) {
    this.order = order;
  }
}
Düzeltmek için şöyle yaparız. Burada User diye yeni bir arayüz yaratıldı ve Customer bu arayüzden kalıtıyor. Order sınıfı da arayüzü kullanıyor
public interface User {}

@Service
public class Order {
  private User user;

  public Order(User user) {
    this.user = user;
  }
}

@Service
public class Customer implements User {
  private Order order;

  public Customer(Order order) {
    this.order = order;
  }
}
Örnek - Kodları Ortak Bir Sınıfa Taşımak
Elimizde şöyle bir kod olsun. Bu kod Circular Dependency olduğu için çalışmaz
@Service
public class Payment {
  private Order order;

  public Payment(Order order) {
    this.order = order;
  }
}

@Service
public class Order {
  private Payment payment;

  public Order(Payment payment) {
    this.payment = payment;
  }
}
Düzeltmek için şöyle yaparız. Burada PaymentService diye yeni bir sınıf yaratıldı ve ortak kodlar buraya taşındı
@Service
public class Payment {
  private PaymentService paymentService;

  public Payment(PaymentService paymentService) {
    this.paymentService = paymentService;
  }
}

@Service
public class Order {
  private PaymentService paymentService;

  public Order(PaymentService paymentService) {
    this.paymentService = paymentService;
  }
}

@Service
public class PaymentService {
  private OrderRepository orderRepository;

  public PaymentService(OrderRepository orderRepository) {
    this.orderRepository = orderRepository;
  }
}
2. @Lazy Hale Getirmek

Bu kullanımda hem A hem de B bean'i halen yaratılır. Ancak Spring A bean'ine B için bir tane proxy verir.

Dolayısıyla bu kullanım yerine önce Field Injection veya Setter Injection tercih edilebilir. Açıklaması şöyle.
Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).
Örnek - @Lazy Field Injection
Elimizde şöyle bir kod olsun. Bu kod Circular Dependency olduğu için çalışmaz
@Service
public class UserService {
  private EmailService emailService;

  public UserService(EmailService emailService) {
    this.emailService = emailService;
  }
}

@Service
public class EmailService {
  private UserService userService;

  public EmailService(UserService userService) {
    this.userService = userService;
  }
}
Düzeltmek için şöyle yaparız
@Service
public class UserService {
  @Lazy
  @Autowired
  private EmailService emailService;

  public void doSomething() {
    emailService.sendEmail();
  }
}

@Service
public class EmailService {
  private UserService userService;

  public EmailService(UserService userService) {
    this.userService = userService;
  }

  public void sendEmail() {
    userService.doSomethingElse();
  }
}
Örnek 
@Lazy Constructor Injection
Şöyle yaparız.
@Autowired
public AService(@Lazy BService bservice) {
    this.bservice = bservice;
}
3. Field Injection
Bu kullanımda hem A hem de B bean'i halen yaratılır. Ancak Spring alanlara değeri sonra atadığı için sorun olmuyor. Yani B sınıfın constructor'ına breakpoint koyarsak bu metodda çıktığımızda this.a alanın null olduğunu görürüz. Field'a değer daha sonra atanır.

Eğer field List<A> olsaydı da Field Injection çalışırdı.

Örnek
Örnekte @Lazy kullanılmış ancak kullanmak zorunda değiliz. Şöyle yaparız.
@Service
class BService{

  @Lazy
  @Autowired
  AService aservice
}
4. Setter Injection
Bu kullanımda hem A hem de B bean'i halen yaratılır. Ancak Spring setter metodları sonra çağırdığı için sorun olmuyor. Yani B sınıfın constructor'ına breakpoint koyarsak bu metodda çıktığımızda this.a alanın null olduğunu görürüz. Field'a değer daha sonra atanır.

Örnek
Elimizde şöyle bir kod olsun. Bu kod Circular Dependency olduğu için çalışmaz
@Service
public class ClassA {
  private ClassB b;

  public ClassA(ClassB b) {
    this.b = b;
  }
}

@Service
public class ClassB {
  private ClassA a;

  public ClassB(ClassA a) {
    this.a = a;
  }
}
Düzeltmek için şöyle yaparız
@Service
public class ClassA {
  private ClassB b;

  public ClassA() {}

  @Autowired
  public void setB(ClassB b) {
    this.b = b;
  }
}

@Service
public class ClassB {
  private ClassA a;

  public ClassB() {}

  @Autowired
  public void setA(ClassA a) {
    this.a = a;
  }
}
5. @PostConstruct Anotasyonu Kullanmak
Örnek ver

SpringWebFlux WebClient.RequestHeadersUriSpec Arayüzü - get, delete, head İçindir

Giriş
Şu Http işleri yapılabilir.
delete()
get()
head()
options()
işleri içindir

exchange metodu
Şöyle yaparız
webClient.get().uri(...).exchange().flatMapMany (...)
retrieve metodu
Get içindir. retrieve() metodundan sonra bodyToFlux() veya bodyToMono() çağrısı yapılarak Flux veya Mono nesnesine çevirmek gerekir.

Örnek
Şöyle yaparız
webClient.get().uri(...).retrieve().bodyToFlux(...).collectList().block();
Örnek - Blocking
Şöyle yaparız
String workerUserName = ...;
LocalDate date = ...;
// retrieve worklogs for worker and day
List<WorklogInfo> worklogsForDay = webClient.get().uri(uriBuilder -> uriBuilder
  .path("/worklogs/worker/{workerUserName}").queryParam("day", date).build(workerUserName))
.retrieve()
.bodyToFlux(WorklogInfo.class)
.collectList()
.block();
Örnek - Stream
Şöyle yaparız
Flux<DataBuffer> stream = webClient
  .get().accept(MediaType.APPLICATION_OCTET_STREAM)
  .retrieve()
  .bodyToFlux(DataBuffer.class);
Path filePath = Paths.get("filename");
AsynchronousFileChannel asynchronousFileChannel = AsynchronousFileChannel.open(filePath,
  WRITE);
return DataBufferUtils.write(stream, asynchronousFileChannel)
  .doOnNext(DataBufferUtils.releaseConsumer())
  .doAfterTerminate(() -> {
    try {
      asynchronousFileChannel.close();
    } catch (IOException ignored) { }
}).then();
onStatus metodu
HttpStatusCode değerlerine göre işlem yapabilmeyi sağlar

Örnek
Şöyle yaparız
public Mono<InventoryEntry> getByName(String name) {
  return webClient
    .get()
    .uri(uriBuilder -> uriBuilder
                        .path("/by-name")
                        .queryParam("title", "{name}")
                        .build(name))
    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .retrieve()
    .onStatus(httpStatusCode -> HttpStatus.NOT_FOUND == httpStatusCode,
      clientResponse -> Mono.empty())
    .onStatus(HttpStatusCode::is5xxServerError, this::handleServerError)
    .bodyToMono(InventoryEntry.class)
    .retryWhen(retrySpec);
}

Mono<ExternalCommunicationException> handleServerError(ClientResponse clientResponse) {
  ExternalCommunicationException returnedException = new ExternalCommunicationException(
    "Communication with inventory client failed due to server error",
    clientResponse.statusCode().value()
  );
  return Mono.error(returnedException);
}

uri metodu
Şöyle yaparız.
client.get().uri(".../{name}", name).accept(MediaType.APPLICATION_OCTET_STREAM)
  ....

SpringWebFlux WebClient.ResponseSpec Arayüzü

bodyToFlux metodu
Şöyle yaparız.
get()
.uri(uri)
.accept(MediaType.APPLICATION_OCTET_STREAM)
.retrieve()
.bodyToFlux(byte[].class)

SpringCloud

Giriş
Sürüm isimlerinin açıklaması şöyle.
The names Celsius, Darwin, Einstein etc are release names of the Spring Cloud Stream App Starters project. ... They are in alphabetical order, ie Einstein is the newer than Darwin (Similar to Android's release naming as Lollipop, Marshmallow, Nougat, Oreo etc).
SpringCloud Abstraction Sağlar
Açıklaması şöyle
... Spring cloud just gives you abstractions over some set of tools (eureka, zuul, feign, ribbon etc.) making it easy for you to integrate with spring applications.

However, you can also achieve microservice architecture without using spring cloud. You can take advantage of tools like Kubernetes, docker swarm, haproxy, Kong, nginx etc to achieve the same. The advantage of not using spring cloud has its own pros and cons and vice versa.
Spring Cloud ile Netflix kullanıyorsak 5 tane önemli anotasyon var. Bunlar şöyle
@EnableConfigServer

@EnableEurekaServer
@EnableEurekaClient

@EnableDiscoveryClient

@EnableCircuitBreaker

@EnableHystrix 
@EnableHystrixDashboard
@HyStrixCommand(fallbackmethod=”MethodName”)

@LoadBalanced

Bunlar aslında şu Netflix bileşenlerine denk geliyor.
Service Discovery (Eureka), Circuit Breaker (Hystrix), Intelligent Routing (Zuul) and Client-Side Load Balancing (Ribbon).
Zuul - API Gateway
Zuul yazısına taşıdım.

16 Haziran 2019 Pazar

SpringSecurity JWT Authentication

Giriş
OAuth ve JWT'nin birlikte kullanımı dışında sadece JWT kullanarak authentication da yapılabilir.

Angular İle Kullanmak
Kullandığımız front end kütüphanesinde (örneğin Angular) bir HttpInterceptor kullanarak tüm dışarı giden (outgoing) Http istekleri için Json Web Token bilgisini eklemek gerekir.

Adımlar
Adım sırası kabaca şöyle.
Hemen hemen aynı adımları izleyen bir başka uygulama burada.

Her iki örnek te token üretmek için io.jsonwebtoken.Jwts kütüphanesini kullanıyor.

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>
JwtAuthenticationRestController Sınıfı
- Uygulama JwtAuthenticationRestController vasıtasıyla iki tane Rest noktası sunar. Bunlar şöyledir

1. http://localhost:8080/authenticate
Açıklaması şöyle.  JwtTokenRequest okur ve JwtTokenResponse döner.
Expose a POST API with mapping /authenticate. On passing the correct username and password, it will generate a JSON Web Token (JWT).

2. http://localhost:8080/refresh
JwtTokenRequest okur ve JwtTokenResponse döner. JwtTokenResponse yerine direkt Response nenesi de dönüleblir.

JwtTokenRequest Sınıfı
Sadece username ve password alanlarından oluşur.

JwtTokenResponse Sınıfı
Sadece token isimli String tipinden bir alandan oluşur.

Jwt Authentication İçin Rest Endpoint
Örnek ver

Jwt Authorization İçin Filter Sınıfı - En Önemli Sınıf
JWT Authorization İçin Filter Sınıfı yazısına taşıdım

JwtUserDetails Sınıfı
UserDetails arayüzünden kalıtan bir JwtUserDetails sınıfı kodlanır. Bu sınıf kullanıcı hakkında bilgileri içerir.

JwUserDetailsService Sınıfı
UserDetailsService arayüzünden  kalıtan bir JwUserDetailsService sınıfı kodlanır. Bu sınıf belirtilen isme ve şifreye sahip JwtUserDetails nesnesini döner veya exception fırlatır.

JwtTokenUtil Sınfı
Token içindeki çeşitli alanları erişimi kolaylaştırır.

WebSecurityConfigurerAdapter  Sınıfı
Şöyle yaparız
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

  @Autowired
  private JwtRequestFilter jwtRequestFilter;

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {

    // dont authenticate this particular request
    httpSecurity.authorizeRequests()
      .antMatchers("/authenticate").permitAll()
      .antMatchers(HttpMethod.OPTIONS, "/**").permitAll().
      // all other requests need to be authenticated
      anyRequest().authenticated()
      .and()
// make sure we use stateless session; session won't be used to
// store user's state.
      .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
      .and()
      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
  }
}
JwtAuthenticationEntryPoint Sınıfı
Şöyle yaparız
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response,
                       AuthenticationException authException) throws IOException {

    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
  }
}