11 Kasım 2020 Çarşamba

SpringWebSocket SimpMessagingTemplate Sınıfı

Giriş
convertAndSendToUser() metodu ile belirli bir kullanıcıya mesaj gönderir.
convertAndSend metodu ile tüm kullanıcılara mesaj gönderir.

SpringData ElasticSearch ElasticsearchRepository Arayüzü

Giriş
Bu arayüz ile üretilen metodlar ElasticSearch açısında "query_string" sorguları oluşturuyor.

Örnek - keyword
Elimizde şöyle bir kod olsun. Burada en öenmli şeylerden birisi FieldType enum değerlerini bilmek. Burada "Keyword" kullanılıyor. Açıklaması şöyle
- The text datatype is used for full-text search and any field mapped as text gets converted into individual words before being indexed.
- The keyword datatype is used for exact matches. The field doesn’t get analyzed and gets stored as it is.
- Elasticsearch has removed the string datatype in versions 5.0 and above and introduced two new datatypes text and keyword. Any string field gets analyzed with both these datatypes. However you can modify the behavior.
Yani bu alan tokenize edilmeyecek 
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "foo_message")
public class Foo {

    @Id
    @Field(type = FieldType.Keyword)
    @JsonProperty("id")
    public String id;

    @JsonProperty("message")
    public String message;

    @JsonProperty("senderUsername")
    public String senderUsername;

    @JsonProperty("senderUserId")
    public Long senderUserId;

    @JsonProperty("receiverUserId")
    public Long receiverUserId;
}
Şöyle yaparız
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface KafkaMessageRepository extends ElasticsearchRepository<Foo, String> {
List<Foo> findAllBySenderUserIdAndReceiverUserIdOrderById(Long senderId, Long receiverId);
}
Örnek - text
Şöyle yaparız
@Document(indexName = "productindex")
public class Product {
  @Id
  private String id;
  
  @Field(type = FieldType.Text, name = "name")
  private String name;
  
  @Field(type = FieldType.Double, name = "price")
  private Double price;
  
  @Field(type = FieldType.Integer, name = "quantity")
  private Integer quantity;
  
  @Field(type = FieldType.Keyword, name = "category")
  private String category;
  
  @Field(type = FieldType.Text, name = "desc")
  private String description;
  
  @Field(type = FieldType.Keyword, name = "manufacturer")
  private String manufacturer;
  ...
}

10 Kasım 2020 Salı

SpringData ElasticSearch @Field Anotasyonu

type Alanı
Double, Integer,  Nested, Text gibi değerler alabilir.

Örnek
Şöyle yaparız
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Document(indexName = "book")
public class Book {
  @Id
  private String id;

  @Field(type = FieldType.Text, name = "title")
  private String title;

  @Field(type = FieldType.Integer, name = "page")
  private Integer page;

  @Field(type = FieldType.Text, name = "isbn")
  private String isbn;

  @Field(type = FieldType.Text, name = "description")
  private String description;

  @Field(type = FieldType.Text, name = "language")
  private String language;

  @Field(type = FieldType.Double, name = "price")
  private Double price;
}
Örnek - Nested
Şöyle yaparız.
@Document(indexName = "blog", type = "article")
public class Article {
  @Id
  private String id;
  private String title;
  @Field(type = FieldType.Nested, includeInParent = true)
  private List<Author> authors;
   // standard getters and setters
}
Açıklaması şöyle
The authors field is marked as FieldType.Nested. This allows us to define the Author class separately, but have the individual instances of author embedded in an Article document when it is indexed in Elasticsearch.
Author sınıfı şöyledir
public class Author {

  @Field(type = Text)
  private String name;
    ...
}

9 Kasım 2020 Pazartesi

SpringCloud Gateway Kullanımı

Giriş
SpringCloud Gateway (SCG) altta Netty kullanır. Şeklen şöyle

Özellikleri
Açıklaması şöyle. Zengi bir Predicate ve Filtre desteği veriyor.
- Rich set of predicate and filter support — provides various predicates like: path, cookie, time, header, host, method, query, remote address, weight etc. Similar to predicates, it provide number of filters like: Add/Set/Remove/Map Request/Response Headers, Set response code, Redirect, Rewrite etc. filters
- Circuit breakers — supports circuit-breakers based on time and re-direct on failure
- TLS & SSL — TLS and SSL can be configured for Gateway and httpClient inside the Gateway
Route Metadata — Routes can be configured with metadata which further can be utilized for processing
- Easy integration with discovery service and load balancing
- Easy metrics with Actuator
- Easy cache implementation

SCG konfigürasyonu
1. SCG konfigürasyonu application.properties veya kodla yapılabilir Açıklaması şöyle
The routing configuration can be created by using pure Java (RouteLocator) or by using properties configuration
- Ayarları dosyadan yapmak için SpringCloud Gateway application.properties Ayarları yazısına bakabilirsiniz veya 
- Kodla yapmak için SpringCloud Gateway RouteLocator Arayüzü yazısına bakabilirsiniz

Zuul 1 vs SCG
Zuul 1 ile SCG arasında önemli bir fark var. Açıklaması şöyle. Yani SCG reactive çalışıyor
However Zuul is a blocking API. A blocking gateway api makes use of as many threads as the number of incoming requests. So this approach is more resource intensive. If no threads are available to process incoming request then the request has to wait in queue.
...
Spring Cloud Gateway is a non blocking API. When using non blocking API, a thread is always available to process the incoming request. These request are then processed asynchronously in the background and once completed the response is returned. So no incoming request never gets blocked when using Spring Cloud Gateway.

Maven
Şu satırı dahil ederiz. SCG içinde genellikle Eureka kullanıldığı için onu da dahil etmekte fayda var
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Kavramlar
3 tane önemli kavram var. Açıklaması şöyle
Route: Think of this as the destination that we want a particular request to route to. It comprises of destination URI, a condition that has to satisfy — Or in terms of technical terms, Predicates, and one or more filters.

Predicate: This is literally a condition to match. i.e. kind of “if” condition..if requests has something — e.g. path=blah or request header contains foo-bar etc. In technical terms, it is Java 8 Function Predicate

Filter: These are instances of Spring Framework WebFilter. This is where you can apply your magic of modifying request or response. There are quite a lot of out of box WebFilter that framework provides. But of course, we are talking about Spring Framework. So, rest easy folks!!! You can always add your own filter with your own logic :)
Bir başka açıklama şöyle
- Route: Route the basic building block of the gateway. It consists of
  - ID
  - destination URI
  - Collection of predicates and a collection of filters
- A route is matched if aggregate predicate is true.
- Predicate: This is similar to Java 8 Function Predicate. Using this functionality we can match HTTP request, such as headers , url, cookies or parameters.
- Filter: These are instances Spring Framework GatewayFilter. Using this we can modify the request or response as per the requirement.
- Yani her Route için bir ID vardır. 
- Gelen istek Route için tanımlı tüm Predicate'lar true dönerse geçerlidir. 
- Gelen isteği veya döndürülen cevabı değiştirmek için Filter'lar kullanılır

Filter Çeşitleri
3 çeşit filtre var
1. GlobalFilter
2. GatewayFilter
3. WebFilter

GlobalFilter
Açıklaması şöyle
Applies to all the routes but not the controllers hosted in the gateway. Spring provides many inbuilt filters for normal use cases and if required we can implement custom filters as well.

For example, we can apply an inbuilt filter ‘AddRequestHeader’ that adds the header ‘request-header’ in all requests with the value ‘request-header-value’ in the following way:

//Apply filter for all routes using springs inbuilt filter //"AddRequestHeader"
spring.cloud.gateway.default-filters[0]=AddRequestHeader=request-header, request-header-value
GatewayFilter
Açıklaması şöyle
For configuring specific routes, we can apply a route-specific filter in the following way:
GatewayFilter yazısına bakabilirsiniz.

WebFilter
Açıklaması şöyle
Applies to all routes as well as the controllers defined in the gateway by us. We need to implement the WebFilter interface in our custom filter and then write our custom logic by overriding the filter() method 
Örnek
Şöyle yaparız
@Component
@Slf4j
public class CustomFilter implements WebFilter {
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    log.trace("inside custom filter");
    ServerHttpRequest request = exchange.getRequest();
    // logic for request validation
    return chain.filter(exchange);
  }
}

Filter İki Amaç İçin Kullanılır
Açıklaması şöyle
There are 2 different types of filters.
Pre Filters — if you want to add or change request object before you pass it down to destination service, you can use these filters.
Post Filters — if you want to add or change response object before you pass it back to client, you can use these filters.

Yani şeklen şöyle
Rate Limiting
SCG ile Rate Limitin de yapılabilir. RequestRateLimiter yazısına bakabilirsiniz

SpringCloud Netflix Zuul - API Gateway Kullanımı

Giriş
Açıklaması şöyle
- Zuul is the front door for all requests from devices and web sites to the backend of the Netflix streaming application.
- Zuul will serve as our API gateway
- Handle dynamic routing
- Zuul is built to enable dynamic routing, monitoring, resiliency and security.
Sürümler
Zuul 1 ve Zuul 2 sürümleri var. 
Zuul 1 "blocking api" kullanır.
Zuul 2 ise "non-blocking api" kullanır çünkü Zuul 2 altta netty kullanıyor. Açıklaması şöyle
Spring Cloud no longer supports Netflix Zuul.
Filter
Açıklaması şöyle
Zuul has mainly four types of filters that enable us to intercept the traffic in different timelines of the request processing for any particular transaction. We can add any number of filters for a particular URL pattern.
1. pre-filters — are invoked before the request is routed
2. post-filers — are invoked after the request has been routed
3. route-filters — are used to route the request
4. error-filters — are invoked when an error occurs while handling the request.
Şeklen şöyle
Kullanım
1. Her Zuul sunucusu aynı zamanda Eureka istemcisi olduğu için
@EnableDiscoveryClient veya @EnableEurekaClient anotasyonu eklenir
2. @EnableZuulProxy anotasyonu eklenir.
 
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>,

SpringData JpaRepository Arayüzü ve EntityManager İlişkisi

Giriş
Bu sınıf altta EntityManager nesnesini kullanır. Dolayısıyla " JPA Entity Lifecycle" geçerlidir.

Döndürülen Nesneler
Halen EntityManager'a bağlıdırlar.

Örnek
Not : Bu örnekteki custom metodu anlamak için JpaRepository İle Custom Method yazısına bakabilirsiniz.

Elimizde şöyle bir kod olsun
public interface UserRepository extends JpaRepository<User, Long> {
}
Bu kodu şöyle kullanalım. Bu kod 1 noktasında yüklenen nesneyi 2 noktasında değiştiriyor. Daha sonraki noktalarda yapılan save() işlemleri de yan etki olarak 2 noktasındaki kirli nesneyi de kaydediyor.
public void updateUser(int id, String name, int changeReqId){
  User mUser = userRepository.findOne(id); //1
  mUser.setName(name); //2

  ChangeRequest cr = changeRequestRepository.findOne(changeReqId);
  ChangeResponse rs = userWebService.updateDetails(mUser); //3

  if(rs.isAccepted()){
    userRepository.saveAndFlush(mUser); //4
  }

  cr.setResponseCode(rs.getCode());
  changeRequestRepository.saveAndFlush(cr); //this call also saves the changes at step 2
}
Düzeltmek için şöyle yaparız. Böylece nesneyi istendiği durumda EntityManager'dan ayırmak mümkün olur.
public interface UserRepositoryCustom {
    ...
   void detachUser(User u);
    ...
}

public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
    ...
}

@Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
    ...
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void detachUser(User u) {
        entityManager.detach(u);
    }
    ...
}

8 Kasım 2020 Pazar

SpringData Jdbc JdbcTemplate.query metodu

Giriş
Bu metodun çok fazla overload edilmiş hali var. Bu yüzden kendi başına bir yazı yapmaya karar verdim. 
- query() metodu her zaman bir List döndürmüyor. Bu yüzden queryForList() metodu tercih edilebilir.
- query() metodunun tam 19 tane overload edilmiş hali var. Kullanımı karışık.
- query() metodunun ResultSetExtractor, RowCallbackHandler, RowMapper alan overload edilmiş halleri var. 

1. RowMapper kullanan metodlar bir List dönerler. Yani RowMapper sadece bir Object döner. Spring bunu List haline getirir.
2. ResultSetExtractor kullanan metodlar ne tipten veri yapısı (Colleciton) döneceklerini kendileri belirtirler. Açıklaması şöyle
JdbcTemplate allows us to translate the SQL result directly into an object or a list of objects by using the RowMapper or ResultSetExtractor interface.

query metodu - sql + RowMapper

query metodu - sql + args + RowMapper
Parametre ile kullanılan basit Select cümleleri içindir. 
Örnek ver

query metodu - sql + ResultSetExtractor
İmzası şöyle
public <T> T query(final String sql, final ResultSetExtractor<T> rse)
Örnek
Şöyle yaparız
jdbcTemplate.query(
  "SELECT id, name, email FROM users",
  (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email"))
).forEach(user -> System.out.println(user.getName()));

query metodu - sql + args + ResultSetExtractor
İmzası şöyle
@Deprecated
@Override
@Nullable
public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse)
Örnek
Şöyle yaparız.
List<Foo> list = 
fdbcTemplate.query(sql, mapParameters, new ResultSetExtractor<List<DataList>>() {

  @Override
  public List<Foo> extractData(ResultSet r) throws SQLException, DataAccessException {
    List<Foo> list = new ArrayList<Foo>();
    Foo foo  = null;
    while(r.next()) {
        foo  = new Foo();
        foo.setName(r.getString("cName"));
        list.add(foo);
    }
    return list;
    }
});