Home Blog Page 75

Code PHP chuẩn convention với PHP CodeSniffer

code php
Code PHP chuẩn convention với PHP CodeSniffer

Bài viết được sự cho phép của tác giả Phạm Bình

Chào các bạn,

Có lẽ không cần dài dòng, nếu chưa biết gì về coding convention thì mình đã có hẳn một bài viết xịn sò là Chuẩn coding convention trong PHP với PSR, bạn có thể tham khảo nếu thấy cần thiết. Còn trong bài viết này, mình sẽ hướng dẫn bạn cách sử dụng công cụ PHP CodeSniffer để kiểm tra convention tự động khi lập trình PHP.

  10 PHP Instagram Scripts & Widgets tốt nhất
  11 cách tăng tốc nhanh cho WordPress bằng file wp-conig.php

I. PHP CODESNIFFER LÀ GÌ?

Vài điểm Hightlight về PHP CodeSniffer như sau:

– PHP CodeSniffer gồm 2 công cụ chính là phpcs, và phpcbf. Trong đó phpcs là công cụ giúp bạn phát hiện lỗi coding convention, còn phpcbf là công cụ giúp bạn tự động tìm và sửa lỗi coding convention – đương nhiên là chỉ sửa được những gì mà nó có thể sửa.
– PHP CodeSniffer là một package PHP, có thể cài đặt bằng composer như nhiều package php bình thường khác, nó có thể cài đặt theo từng dự án, hoặc có thể cài global trên máy tính của developer.
– PHP CodeSniffer kiểm tra lỗi coding convention dựa trên các PSR, và nếu muốn bạn cũng có thể tự tắt/bật một số quy tắc của PSR để phù hợp với từng dự án, từng team phát triển.
– Bạn có thể dễ dàng tích hợp PHP CodeSniffer với các editor phổ biến như Sublime Text, VsCode,… trong bài viết này, mình cũng sẽ hướng dẫn các bạn các tích hợp với VsCode.

Tìm việc làm PHP đãi ngộ tốt trên TopDev

II. CÀI ĐẶT VÀ SỬ DỤNG PHP CODESNIFFER

2.1 Chuẩn bị

  • PHP 5.4 hoặc phiên bản cao hơn: PHP CodeSniffer được viết bằng PHP, nên máy của bạn chắc chắn cần được cài PHP. Kiểm tra phiên bản PHP bằng cách gõ php -v trên CLI của bạn.
  • Composer: Có nhiều cách để cài đặt PHP CodeSniffer, nhưng bạn nên cài thông qua Composer cho đơn giản, cũng như dễ dàng cập nhật PHP CodeSniffer khi có phiên bản mới. Mà mình nghĩ chẳng có php developer nào lại không có sẵn composer trên máy đâu. Kiểm tra composer đã có hay chưa bằng cách gõ composer trên CLI của bạn.

2.2 Cài đặt PHP CodeSniffer

Như mình đã trình bày, CodeSniffer có thể cài global hoặc cài theo từng dự án, trong bài viết này mình sẽ cài global luôn cho máu.

composer global require "squizlabs/php_codesniffer=*"

Sau khi cài xong, bạn cần tìm được thư mục home của composer, tùy vào từng hệ điều hành mà đường dẫn tới thư mục home sẽ khác nhau. Để tìm thư mục home, bạn chạy lệnh sau:

composer config --list --global | grep "home"

Kết quả sẽ dạng như sau:

[home] /Users/admin/.composer

Thì /Users/admin/.composer chính là thư mục home của composer.

Bước tiếp theo, cùng kiểm tra xem PHP CodeSniffer đã được cài đặt hay chưa, bằng cách chạy lệnh sau:

ls /Users/admin/.composer/vendor/bin

Nếu cài đặt thành công, thì sẽ có kết quả như sau:

phpcbf phpcs

2.3 Chạy thử PHP CodeSniffer

Đây là phần trọng tâm của bài viết, các bạn đọc cẩn thận và làm theo ví dụ nhé.

Tạo trước một file PHP và cố tình code sai convention, để thử xem thằng PHP CodeSniffer có nhận ra hay không.

cd ~ # di chuyển ra thư mục root
mkdir test_php_sniffer # tạo thư mục test_php_sniffer
cd test_php_sniffer # di chuyển vào thư mục vừa tạo
touch test.php # tạo file test.php

Copy nội dung sau bỏ vào file test.php

// File này cố tình code sai convention để thử độ tin cậy của PHP CodeSniffer

function sum ($a ,$b){
return $a+$b;
}

echo sum(1,2);

Chuẩn bị đã xong, giờ mình sẽ chạy lệnh sau để kiểm tra lỗi convention có trong file test.php trên.

cd test_php_sniffer # di chuyển vào thư mục chứa file test.php

/Users/admin/.composer/vendor/bin/phpcs test.php # chạy này

Chạy xong, trên terminal xuất hiện kết quả như sau:

FILE: /Users/admin/test_php_sniffer/test.php
--------------------------------------------------------------------------
FOUND 5 ERRORS AFFECTING 3 LINES
--------------------------------------------------------------------------
2 | ERROR | [ ] You must use "/**" style comments for a file comment
4 | ERROR | [ ] You must use "/**" style comments for a function comment
4 | ERROR | [x] Expected 0 spaces before opening parenthesis; 1 found
4 | ERROR | [x] Opening brace should be on a new line
8 | ERROR | [x] No space found after comma in argument list
--------------------------------------------------------------------------
PHPCBF CAN FIX THE 3 MARKED SNIFF VIOLATIONS AUTOMATICALLY
--------------------------------------------------------------------------

Time: 51ms; Memory: 4MB

Kết quả trên có 2 chỗ cần chú ý:

  • FOUND 5 ERRORS AFFECTING 3 LINES: Phát hiện được 5 lỗi trên 3 dòng.
  • PHPCBF CAN FIX THE 3 MARKED SNIFF VIOLATIONS AUTOMATICALLY: Nếu sử dụng phpcbf thì nó nó thể tự động fix được 3 lỗi.

Thử chạy nốt thằng phpcbf để xem nó tự động fix lỗi như thế nào nhé.

/Users/admin/.composer/vendor/bin/phpcbf test.php

Sau khi chạy xong lệnh trên, file test.php sẽ tự động được sửa lại, thành như sau:

Sau khi chạy phpcbf, nhưng vẫn còn tồn đọng một số lỗi convention

Đúng là code được sửa lại thật, nhưng có vẻ vẫn chưa đúng lắm, vẫn có 2 lỗi chưa fix được:

  • Dòng 4 vẫn bị sai convention ở dấu phẩy. Dấu phẩy phải nằm sát biến $a, và cách biến $b một dấu space mới đúng.
  • Dòng 6 vẫn bị sai convention ở dấu +. Phía trước và sau dấu + phải có một dấu space mới đúng.

– Ơ thế hóa ra thằng CodeSniffer này không được việc lắm nhỉ, có lỗi thì fix được có lỗi thì không?

– Không phải đâu nhé, là do mình chưa chỉ rõ chuẩn PSR cho nó thôi. Phần sau đây mình sẽ trình bày luôn.

Bạn tạo tiếp file tên là phpcs.xml ngang hàng với file test.php trên, có nội dung như sau:

<?xml version="1.0"?> <ruleset name="PHP Standards">
   <rule ref="PSR12" /> </ruleset>

Sau đó chạy lại lệnh phpcbf để fix lỗi:

/Users/admin/.composer/vendor/bin/phpcbf test.php

Sau đó cùng xem lại file test.php để xem nó được fix lỗi như thế nào.

Tất cả lỗi convention đã được fix

Bất ngờ chưa, các lỗi convention được fix hết luôn rồi kìa.

Fix được là do file phpcs.xml mình đã chỉ ra code được tuân theo chuẩn PSR12, nhờ đó mà thằng phpcbf sẽ biết đường mà fix theo chuẩn đó.

Qua ví dụ trên, mình đã chỉ các bạn đi qua một lượt các tính năng cơ bản của PHP CodeSniff, bạn có thể tìm hiểu chi tiết hơn ở wiki của PHP_CodeSniffer.

Bộ sưu tập áo thun cho dân IT, đủ các ngôn ngữ lập trình và hệ điều hành.
Click vào ảnh để xem.

III. LỜI KẾT

Bài viết này cũng khá dài rồi, nên mình tạm dừng ở đây, nhưng dự kiến sẽ còn 2 bài viết nữa xoay quanh thằng PHP CodeSniffer này. Một bài mình sẽ chia sẻ các rules trong file phpcs.xml, một bài mình sẽ chia sẻ cách tích hợp PHP_CodeSniffer với VsCode để vừa code vừa kiểm tra lỗi convention tiện lợi hơn. Hẹn gặp lại các bạn nhé.

Bài viết gốc được đăng tải tại phambinh.net

Có thể bạn quan tâm:

Truy cập ngay việc làm IT đãi ngộ tốt trên TopDev

Cài đặt FTP server sử dụng Docker

cài đặt fpt server
Cài đặt FTP server sử dụng Docker

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Mình đã hướng dẫn các bạn cách cài đặt FTP server trên Ubuntu. Trong bài viết này, mình sẽ hướng dẫn các bạn cài đặt FTP server sử dụng Docker các bạn nhé!

Chúng ta sẽ sử dụng vsftpd Docker Image ở địa chỉ Docker Hub https://hub.docker.com/r/fauria/vsftpd/.

  20 trường hợp sử dụng lệnh Docker cho developer
  Cách thiết lập một dự án Symfony để làm việc với Docker Subdomains

Xem thêm các chương trình tuyển dụng PHP lương cao trên TopDev

Docker Image này expose nhiều biến môi trường khác nhau giúp chúng ta có thể cấu hình FTP server theo cách mình muốn, bao gồm:

  • FTP_USER
  • FTP_PASS
  • PASV_ADDRESS
  • PASV_ADDR_RESOLVE
  • PASV_ENABLE
  • PASV_MIN_PORT
  • PASV_MAX_PORT
  • XFERLOG_STD_FORMAT
  • LOG_STDOUT
  • FILE_OPEN_MODE
  • LOCAL_UMASK
  • REVERSE_LOOKUP_ENABLE
  • PASV_PROMISCUOUS
  • PORT_PROMISCUOUS

Chi tiết ý nghĩa của mỗi biến môi trường, các bạn có thể đọc thêm tại đây.

Mình sẽ sử dụng default value cho các biến môi trường này nên mình chỉ cần chạy command sau là có thể start được 1 FTP server:

docker pull fauria/vsftpd

và:

docker run -d -p 21:21 -v /Users/khanh/Documents/vsftpd:/home/vsftpd --name vsftpd fauria/vsftpd

Mình mapping thư mục trên máy của mình với thư mục /home/vsftpd để chúng ta có thể dễ dàng làm việc với data trên FTP server này.

Kết quả:

Như các bạn thấy, sau khi chạy docker run command thì mình cũng chạy thêm một command nữa là docker logs để xem thông tin mặc định mà Docker Image này khởi tạo khi chạy Docker Container từ nó. Các bạn có thể sử dụng thông tin username và password được hiển thị ở đây để đăng nhập và làm việc với FTP server, các bạn nhé!

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Xem thêm việc làm CNTT hấp dẫn trên TopDev

Selenium IDE – Các câu lệnh kiểm tra

selenium ide
Selenium IDE – Các câu lệnh kiểm tra

Bài viết được sự cho phép của vntesters.com

Chào các bạn, như mình đã giới thiệu ở phần trước, Selenium IDE hầu như chỉ record lại những hành động của chúng ta trên trình duyệt. IDE không đủ thông minh để biết được chúng ta muốn kiểm tra những gì trên trang web đó. Bài này, mình sẽ giới thiệu với các bạn một số câu lệnh kiểm tra hay dùng trong Selenium IDE để kiểm tra các đối tượng UI trên trang web.

  Các kiểu “đợi chờ” trong Selenium Webdriver: Implicit wait, Explicit wait và Fluent wait
  Firefox profile preferences để download file với Selenium Webdriver

Xem thêm tuyển Data Science hấp dẫn trên TopDev

Selenium IDE – Tự động kiểm tra

Theo như mình biết thì hiện tại Selenium IDE chỉ hỗ trợ kiểm tra tiêu đề của trang web một cách tự động. Để làm được điều đó, chúng ta cần thiết lập cơ chế của IDE như sau:

B1: Mở Selenium IDE Options bằng menu item Options –> Options

B2: Kích hoạt chức năng “Record assertTitle automatically” trong thẻ General

tu dong kiem tra tieu de

Như vậy, khi chúng ta thực thi record với Selenium IDE, cứ mỗi trang mới được hiển thị, IDE sẽ tự động thêm dòng lệnh kiểm tra tiêu đề trang web cho chúng ta.

Selenium IDE – Các câu lệnh kiểm tra

Để thêm các câu lệnh vào Selenium IDE, các bạn có thể chọn menu item Edit –> Insert New Command, hoặc chọn Insert New Command ở Context Menu

thêm câu lệnh kiểm tra

Các câu lệnh về đối tượng UI

Đối với các đối tượng UI, thông thường chúng ta chỉ có hai loại kiểm tra: Tồn tại hay không tồn tại.

  • verifyElementPresent/assertElementPresent: Hai câu lệnh này dùng để kiểm tra một đối tượng UI tồn tại trên trang web.
  • verifyElementNotPresent/assertElementNotPresent: Hai câu lệnh này dùng để kiểm tra một đối tượng UI không tồn tại trên trang web.

Bên cạnh hai loại kiểm tra chính, Selenium IDE còn hỗ trợ chúng ta kiểm tra đối tượng UI ở mức độ chi tiết hơn như:

Cách hiển thị của đối tượng UI trên trang web:

  • Chiều cao: verifyElementHeight/assertElementHeight – verifyNotElementHeight/assertNotElementHeight
  • Chiều rộng: verifyElementWidth/assertElementWidth – verifyNotElementWidth/assertNotElementWidth
  • Toạ độ trái so với trang web: verifyElementPositionLeft/assertElementPositionLeft – verifyNotElementPositionLeft/assertNotElementPositionLeft
  • Toạ độ trên so với trang web: verifyElementPositionTop/assertElementPositionTop – verifyNotElementPositionTop/assertNotElementPositionTop

Các đối tượng UI được thiết kế với thuộc tính đặc biệt và chúng ta cần kiểm tra thuộc tính đó: verifyAttribute/assertAttribute – verifyNotAttribute/assertNotAttribute

Đối tượng UI đặc biệt như Checkbox hay RadioBox: verifyChecked/assertChecked – verifyNotChecked/assertNotChecked

Các câu lệnh về text

Đôi khi, chúng ta không cần kiểm tra một đối tượng UI, chúng ta chỉ muốn biết một dòng text có hay không có xuất hiện trên trang web. Lúc này, chúng ta có các câu lệnh kiểm tra vể text từ Selenium IDE.

  • verifyTextPresent/assertTextPresent: Hai câu lệnh này dùng để kiểm tra một dòng text tồn tại trên trang web.
  • verifyTextNotPresent/assertTextNotPresent: Hai câu lệnh này dùng để kiểm tra một dòng text không tồn tại trên trang web.

Vì bốn câu lệnh kiểm tra text này không tương tác với đối tượng UI nào, nên phần Target của câu lệnh chính là dòng text mà chúng ta muốn kiểm tra.

Trong trường hợp đặc biệt, chúng ta muốn kiểm tra text của một đối tượng UI xác định, chúng ta có hai câu lệnh verifyText/assertText. Lúc này, target chính là đối tượng UI của chúng ta, và Value chính là dòng text cần kiểm tra.

Các câu lệnh vể tổng thể trang web

Khi chúng ta quan trọng việc thiết kế trang web một cách tổng thể, Selenium IDE hỗ trợ chúng ta các câu lệnh có thể làm việc với toàn bộ trang web.

  • verifyBodyText/assertBodyText: Kiểm tra toàn bộ text trên trang web.
  • verifyCssCount/assertCssCount: Kiểm tra số lượng đối tượng UI sử dụng chung một thiết kế CSS
  • verifyXpathCount/assertXpathCount: Kiểm tra số lượng đối tượng UI có cùng XPath

Trên đây là một số câu lệnh dùng để kiểm tra trang web với Selenium IDE mà mình nghĩ là chúng ta thường xuyên sử dụng. Ngoài ra, Selenium IDE còn hỗ trợ chúng ta rất nhiều câu lệnh để chúng ta có thể sủ dụng trong các trường hợp đặc biệt mà các bạn có thể tìm thấy trong ô Command trên màn hình chính của Selenium IDE.

Bài viết gốc được đăng tải tại vntesters.com

Có thể bạn quan tâm:

Xem thêm việc làm CNTT hấp dẫn trên TopDev

Semaphore trong Java

Semaphore trong Java
Semaphore trong Java

Bài viết được sự cho phép của tác giả Giang Phan

Semaphore là gì?

Semaphore là một cơ chế giúp quản lý các nguồn chia sẻ và đảm bảo access không bị tắc nghẽn.

Có hai loại semaphore: binary semaphore và counting semaphore.

  • Binary semaphore (Mutex): được dùng làm lock vì nó chỉ có 2 giá trị là 0 và 1. Hai giá trị này đại diện cho trạng thái lock hay unlock.
  • Counting semaphore: thực hiện đếm resource để cho biết mức độ sẵn sàng của resource.

Xem thêm tuyển dụng Java lương hấp dẫn trên TopDev

Cơ chế hoạt động

Một Semaphore lưu trữ một danh sách các permit (hay ticket), mỗi khi gọi acquire() sẽ lấy 1 ticket từ Semaphore, mỗi khi gọi release() sẽ trả ticket về Semaphore. Nếu ticket không có sẵn, acquire() sẽ bị lock cho đến khi có ticket. Để kiểm tra số lượng ticket còn lại, sử dụng phương thức availablePermits().

Ví dụ chúng ta gọi các phương thức tuần tự như sau:

// Tạo một Semaphore có 5 ticket
Semaphore semaphore = new Semaphore(5);

// Yêu cầu lấy 1 ticket để sử dụng
semaphore.acquire(); // 5-1

// Đếm về số lượng ticket có sẵn
int numberOfAvailableTickets = semaphore.availablePermits(); // 4

// Trả 1 ticket về Semaphore
semaphore.release(); // 4+1

// Đếm về số lượng ticket có sẵn
semaphore.availablePermits(); // 5

Ví dụ sử dụng Semaphore

Giả sử một ngân hàng có 4 cây ATM, mỗi cây chỉ có thể phục vụ được một khách hàng tại một thời điểm. Chương trình bên dưới cho thấy Semaphore có thể đảm bảo chỉ tối đa 4 người có thể truy cập tại một thời điểm.

WorkerThread.java

package com.gpcoder.semaphore;

import java.util.concurrent.Semaphore;

public class WorkerThread extends Thread {

private final Semaphore semaphore;
private String name;

public WorkerThread(Semaphore semaphore, String name) {
this.semaphore = semaphore;
this.name = name;
}

public void run() {
try {
System.out.println(name + ": acquiring lock...");
System.out.println(name + ": available Semaphore permits now: " + semaphore.availablePermits());
semaphore.acquire();
System.out.println(name + ": got the permit!");

try {
System.out.println(name + ": is performing operation, available Semaphore permits : "
+ semaphore.availablePermits());
Thread.sleep(100); // simulate time to work
} finally {
// calling release() after a successful acquire()
System.out.println(name + ": releasing lock...");
semaphore.release();
System.out.println(name + ": available Semaphore permits now: " + semaphore.availablePermits());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

SemaphoreExample.java

package com.gpcoder.semaphore;

import java.util.concurrent.Semaphore;

public class SemaphoreExample {

private static Semaphore semaphore = new Semaphore(4);

public static void main(String[] args) {
System.out.println("Total available Semaphore permits: " + semaphore.availablePermits());
for (int i = 1; i <= 6; i++) {
WorkerThread atmWorker = new WorkerThread(semaphore, "AMT " + i);
atmWorker.start();
}
}
}

Chạy chương trình trên, ta có kết quả sau:

Total available Semaphore permits: 4
AMT 1: acquiring lock...
AMT 1: available Semaphore permits now: 4
AMT 1: got the permit!
AMT 1: is performing operation, available Semaphore permits : 3
AMT 2: acquiring lock...
AMT 2: available Semaphore permits now: 3
AMT 2: got the permit!
AMT 2: is performing operation, available Semaphore permits : 2
AMT 3: acquiring lock...
AMT 3: available Semaphore permits now: 2
AMT 3: got the permit!
AMT 3: is performing operation, available Semaphore permits : 1
AMT 6: acquiring lock...
AMT 4: acquiring lock...
AMT 4: available Semaphore permits now: 1
AMT 4: got the permit!
AMT 4: is performing operation, available Semaphore permits : 0
AMT 5: acquiring lock...
AMT 5: available Semaphore permits now: 0
AMT 6: available Semaphore permits now: 1
AMT 3: releasing lock...
AMT 4: releasing lock...
AMT 1: releasing lock...
AMT 4: available Semaphore permits now: 2
AMT 2: releasing lock...
AMT 2: available Semaphore permits now: 2
AMT 6: got the permit!
AMT 6: is performing operation, available Semaphore permits : 2
AMT 5: got the permit!
AMT 5: is performing operation, available Semaphore permits : 2
AMT 1: available Semaphore permits now: 3
AMT 3: available Semaphore permits now: 2
AMT 6: releasing lock...
AMT 6: available Semaphore permits now: 3
AMT 5: releasing lock...
AMT 5: available Semaphore permits now: 4

Ví dụ sử dụng Mutex

Mutex là một Semaphore với bộ đếm là 1. Tình huống có thể sử dụng là lock tài khoản khi rút tiền. Tại một thời điểm chỉ 1 thao tác rút tiền được chấp nhận.

Chúng ta sẽ sử dụng lại worker ở trên. Giả sử có 6 user cùng login vào một tài khoản ở các cây ATM khác nhau để thực hiện rút tiền, nếu chúng ta không sử dụng cơ chế synchronized thì cả 6 người đều có thể rút tiền cùng lúc và có thể rút nhiều hơn số tiền hiện có trong tài khoản.

Đoạn code bên dưới sử dụng Mutex giúp chúng ta kiểm soát được vấn đề này một cách dễ dàng.

package com.gpcoder.semaphore;

import java.util.concurrent.Semaphore;

public class MutexExample {

private static Semaphore semaphore = new Semaphore(1);

public static void main(String[] args) {
System.out.println("Total available Semaphore permits: " + semaphore.availablePermits());
for (int i = 1; i <= 6; i++) {
WorkerThread atmWorker = new WorkerThread(semaphore, "AMT " + i);
atmWorker.start();
}
}
}

Chạy chương trình trên, chúng ta có kết quả sau:

Total available Semaphore permits: 1
AMT 1: acquiring lock...
AMT 2: acquiring lock...
AMT 1: available Semaphore permits now: 1
AMT 2: available Semaphore permits now: 1
AMT 1: got the permit!
AMT 1: is performing operation, available Semaphore permits : 0
AMT 3: acquiring lock...
AMT 3: available Semaphore permits now: 0
AMT 4: acquiring lock...
AMT 4: available Semaphore permits now: 0
AMT 5: acquiring lock...
AMT 6: acquiring lock...
AMT 6: available Semaphore permits now: 0
AMT 5: available Semaphore permits now: 0
AMT 1: releasing lock...
AMT 1: available Semaphore permits now: 1
AMT 2: got the permit!
AMT 2: is performing operation, available Semaphore permits : 0
AMT 2: releasing lock...
AMT 2: available Semaphore permits now: 0
AMT 3: got the permit!
AMT 3: is performing operation, available Semaphore permits : 0
AMT 3: releasing lock...
AMT 3: available Semaphore permits now: 1
AMT 4: got the permit!
AMT 4: is performing operation, available Semaphore permits : 0
AMT 4: releasing lock...
AMT 6: got the permit!
AMT 6: is performing operation, available Semaphore permits : 0
AMT 4: available Semaphore permits now: 0
AMT 6: releasing lock...
AMT 5: got the permit!
AMT 5: is performing operation, available Semaphore permits : 0
AMT 6: available Semaphore permits now: 0
AMT 5: releasing lock...
AMT 5: available Semaphore permits now: 1

Bài viết đến đây là hết. Hy vọng sau bài viết này các bạn hiểu rõ hơn về Semaphore, cũng như biết cách sử dụng nó trong một số tình huống thích hợp.

Tài liệu tham khảo:

Cơ bản về Apache Kafka

apache kafka
Cơ bản về Apache Kafka

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Apache Kafka là một open-source distributed event streaming platform giúp chúng ta có thể tạo và process data stream real-time. Các bạn có thể hình dung đến những hệ thống mà mỗi giây, data đều được sinh ra và cần được xử lý ví dụ như hệ thống quản lý giao thông, quản lý thời tiết, data sẽ được tạo ra từ nhiều nơi khác nhau và cần được collect để xử lý. Sử dụng Apache Kafka với high scalable có thể giúp chúng ta giải quyết những bài toán như vậy.

  Cài đặt Apache Kafka trên macOS
  Sử dụng Apache POI để đọc, ghi dữ liệu từ file Excel trong Selenium

Xem thêm các việc làm Flutter lương cao trên TopDev

Apache Kafka là một hệ thống enterprise messaging, hoạt động với cơ chế Pub-Sub messaging architecture. Chúng ta có Message Publisher, Message Consumer và Message Broker. Message Publisher là nơi produce các message và emit các message vào Message Broker, Message Consumer là nơi nhận, nó sẽ subscribe Message Broker và nhận các message của Message Publisher thông qua Message Broker.

Apache Kafka Server sẽ đóng vai trò là một Message Broker.

Apache Kafka cung cấp cho chúng ta các library để làm việc với Publisher và Subscriber. Ngoài ra, Apache Kafka còn có thêm các component khác như Kafka Connect, Kafka Streams.

Apache Kafka được xây dựng từ đầu bởi LinkedIn, một mạng xã hội về việc làm, và được open-source từ năm 2011.

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Xem thêm thông tin việc làm CNTT hấp dẫn trên TopDev

Viết Thư Từ Chối Phỏng Vấn Bằng Tiếng Anh Như Thế Nào Cho Chuyên Nghiệp?

viết thư từ chối phỏng vấn bằng tiếng anh
Viết Thư Từ Chối Phỏng Vấn Bằng Tiếng Anh Như Thế Nào Cho Chuyên Nghiệp?

Sau khi đã nhận được kết quả ứng tuyển thành công vào một công ty, có thể bằng nhiều lý do khác nhau mà bạn cảm thấy mình không thể tiếp nhận phỏng vấn vị trí đó như dự định, làm sao để có thể từ chối một cách lịch sự nhất? Đặc biệt, trong một số trường hợp, bạn còn phải viết thư bằng tiếng anh, vậy đâu là cách viết thư từ chối phỏng vấn bằng tiếng Anh chuyên nghiệp và khéo léo? Bài viết dưới đây sẽ giúp bạn có thêm thông tin về vấn đề này.

viết thư từ chối phỏng vấn bằng tiếng Anh
Cách viết thư từ chối phỏng vấn bằng tiếng Anh

Cấu trúc cần có khi viết thư từ chối phỏng vấn bằng tiếng Anh

Cũng như cách bạn nộp đơn ứng tuyển vào công ty, khi viết đơn từ chối bất cứ vị trí nào cũng hãy thể hiện một nội dung chuyên nghiệp với bố cục rõ ràng. Nó sẽ giúp bạn xây dựng được ấn tượng tốt trong mắt nhà tuyển dụng và biết đâu bạn có thể quay lại công ty ở một vị trí khác trong tương lai. Trên hết, nó giúp tiết kiệm thời gian và công sức chờ đợi của cả hai phía. Theo đó, bố cục email sẽ gồm có:

Xem thêm các việc làm PHP lương cao trên TopDev

  • Tiêu đề email: Có 2 cách để bạn viết thư từ chối nhà tuyển dụng. Nếu bạn trả lời trực tiếp trên email mời phỏng vấn nhà tuyển dụng gửi đến thì không cần có tiêu đề. Trong trường hợp bạn viết một email mới, cần viết tiêu đề một cách ngắn gọn, rõ ràng, khái quát được mục đích bạn viết bức thư này. Chẳng hạn “Thank you for the interview letter”, “Reply for the interview invitation”,…
  • Lời chào và cảm ơn: Đây là yếu tố cực kỳ cần thiết và cũng là cách mở đầu bức thư của bạn. Gửi lời chào trực tiếp đến người gửi thư phỏng vấn cho bạn hoặc chức danh vị trí của họ – Dear HR Manager,… để thể hiện sự tôn trọng với người đọc.

Hãy gửi lời cảm ơn đến họ vì đã lựa chọn bạn cho vị trí này. Nhân sự và các phòng ban liên quan đã dành khá nhiều thời gian để chọn lọc hồ sơ và tin tưởng chọn bạn cho vị trí này, do đó trong trường hợp không thể tiếp nhận phỏng vấn, việc gửi lời cảm ơn đến họ sẽ khiến nhà tuyển dụng đỡ cảm thấy hụt hẫng và có ấn tượng tốt về bạn hơn.

  • Thank you very much for the interview invitation at A Company. I appreciate being considered as a candidate with the B position.
  • I’m grateful for the opportunity to interview at A Company with the B position.

Xem thêm Phỏng Vấn Data Analyst: TOP 5 Câu Hỏi Bạn Không Nên Bỏ Qua!

  • Trình bày lý do từ chối phỏng vấn: Đây là nội dung chính của bức thư. Có thể có nhiều lý do khác nhau khiến bạn quyết định từ chối lời mời, nhưng không ai biết được trong tương lai bạn có thể gặp lại công ty ở thời điểm nào đó, nên cách tốt nhất là hãy giữ lại ấn tượng tốt đẹp trong nhau. Hãy cố gắng lựa chọn một lý do từ chối nhẹ nhàng và đủ sức thuyết phục.
  • Gửi lời cảm ơn và lời chúc đến nhà tuyển dụng để kết thúc bức thư.
  • Thank you again and hope you will find a suitable candidate as soon as possible.
  • I wish you all the best in your search for a candidate.
thư từ chối
Một số điều cần có trong thư từ chối

Một số lưu ý khi viết thư từ chối phỏng vấn bằng tiếng Anh

Bạn nên cố gắng xem xét thật kỹ và sớm nhất lời mời phỏng vấn, và nếu không muốn tiếp nhận phỏng vấn hãy phản hồi bằng thư từ chối sớm nhất có thể, tốt nhất là trong khoảng 24 giờ. Việc này sẽ giúp nhà tuyển dụng tìm được ứng viên khác và tiến hành phỏng vấn, tránh lãng phí thời gian chờ đợi của cả 2 bên.

Về lý do từ chối, như đã nói ở trên, đừng đưa ra một lý do quá khách quan và không có gì đảm bảo tính xác thực của nó, như tin đồn về môi trường công ty không tốt hay phúc lợi công ty còn thấp,… Những lý do này sẽ rất dễ khiến bạn rơi vào “blacklist” của công ty và nó thật sự không tốt cho tương lai của bạn sau này.

  Văn hóa giao tiếp qua Email - Thế nào là chuyên nghiệp?
  Bí quyết phát triển hoạt động nhân sự qua email (Email Marketing) hiệu quả - Bạn đã biết?

Văn phong sử dụng trong bức thư cần lịch sự và chuyên nghiệp nhất có thể. Điều này không chỉ giúp bạn xây dựng ấn tượng tốt trong mắt nhà tuyển dụng mà biết đâu, trong tương lai khi bạn quay lại công ty ở một vai trò khác và điều này sẽ không gây cản trở đến công việc của bạn.

Ngoài ra, nếu được, bạn hoàn toàn có thể giới thiệu một ứng viên khác cho công ty. Hãy đưa ra vài thông tin cơ bản về ứng viên này cũng như để lại cách thức liên lạc trong bức thư. Nhà tuyển dụng sẽ quyết định việc lựa chọn và tiến hành các bước tiếp theo.

Một số mẫu email tham khảo

Một số mẫu viết thư từ chối phỏng vấn bằng tiếng Anh bạn có thể tham khảo:

email mẫu

Mẫu 1:

Dear Ms. Han – HR Manager of A Company,

I’m very appreciative of the interview opportunity for Content Marketing and learning more about your organization.

However, I regret that I have to deny this opportunity at this time. Because I was offered and accepted a job at another company, so I’m respectfully declining this offer.

Thank you again and hope you will find a suitable candidate as soon as possible.

Best regards,

Mẫu 2:

Dear Ms. Jennie,

Thank you so much for reaching out and send me the interview invitation,

However, I would like to withdraw my application for this position because of personal reasons.

I will keep this role in mind and refer anyone who may be a good match.

Thank you again for considering me for this position. Please do not hesitate to get in touch if you have any questions.

Thanks and Best regards,

Tùy theo mỗi lý do cá nhân khác nhau, mọi người sẽ có cách riêng để viết thư từ chối phỏng vấn. Tuy nhiên, về cơ bản cần đảm bảo đủ các yếu tố như trên để không làm khó chịu người nhận. Đón đọc thêm nhiều nội dung hấp dẫn khác cùng Topdev.vn/blog nhé!

Có thể bạn quan tâm:

Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Thu thập và làm sạch dữ liệu khi sử dụng Linear Regression

linear regression
Thu thập và làm sạch dữ liệu khi sử dụng Linear Regression

Bài viết được sự cho phép của tác giả Kien Dang Chung

Video trong bài viết

Trước khi bắt đầu bài đầu tiên thu thập và làm sạch dữ liệu chuẩn bị cho thuật toán Linear Regression, chúng ta sẽ tìm hiểu qua quy trình làm việc khoa học dữ liệu.

  Ấn tượng trước những bước đột phá công nghệ mới của Line!
  Cơ hội nào cho "dân buôn" online?

Xem thêm tuyển 3D Artist hấp dẫn trên TopDev

Quy trình làm việc khoa học dữ liệu

Bất kể bạn áp dụng thuật toán Machine Learning nào cho vấn đề bạn gặp phải, chúng ta sẽ đều phải trải qua một quy trình làm việc (data science workflow).

Quy trình làm việc khoa học dữ liệu

Trong tất cả các dự án của Khóa học Machine Learning từ A đến Z, chúng ta sẽ áp dụng quy trình này, tuy rằng không phải lúc nào cũng đủ tất cả các bước trong đấy. Trong quy trình này có 3 giai đoạn và rất nhiều các bước.

  • Giai đoạn 1 – Chuẩn bị dữ liệu: Dữ liệu được thu thập và làm sạch. Phần lớn dữ liệu gốc đều ở dạng hỗn loạn, có thể thiếu thông tin hoặc thông tin sai lệch, do vậy cần được xử lý trước khi đưa vào các mô hình thuật toán.
  • Giai đoạn 2 – Thử nghiệm: tại giai đoạn này các giả định được đặt ra, dữ liệu được trực quan hóa thông qua các biểu đồ và lựa chọn các mô hình.
  • Giai đoạn 3 – Triển khai: Các báo cáo, đánh giá quá trình áp dụng và kết quả được triển khai thông qua các ứng dụng thực tế hoặc đơn thuần là các báo cáo.

Khi thực hiện các giai đoạn, có thể quay lại giai đoạn trước đó để chuẩn bị dữ liệu tốt hơn. Quy trình này được áp dụng cho các công việc thực tế, nhưng với các bài hướng dẫn trong Machine Learning AZ chúng ta sẽ rút gọn các bước lại để tập trung vào việc học tập hơn.

Các bước thực hiện trong Machine Learning A đến Z

Các bước được cô đọng lại như sau:

  • Bước 1: Hình thành câu hỏi
  • Bước 2: Thu thập dữ liệu
  • Bước 3: Làm sạch dữ liệu
  • Bước 4: Khám phá và trực quan hóa dữ liệu với biểu đồ
  • Bước 5: Áp dụng thuật toán hay mô hình huấn luyện
  • Bước 6: Đánh giá kết quả

Bài toán dự đoán doanh thu phim

Trước khi bắt đầu xử lý một vấn đề, chúng ta nên bỏ chút thời gian đặt ra những câu hỏi như Cái gì, Tại sao, Làm như thế nào, Ở đâu… những câu hỏi này giúp chúng ta trìu tượng hóa vấn đề. Cũng chính nhờ những câu hỏi này chúng ta sẽ xác định được loại dữ liệu nào là cần thiết bởi trong thực tế lượng dữ liệu là rất khổng lồ.

Trở lại với vấn đề mà chúng ta đang gặp phải trong bài toán dự đoán doanh thu phim. Những nhà đầu tư phim, họ rót vốn cho các bộ phim và quan tâm đến lợi nhuận các bộ phim. Áp dụng quy trình data science ở trên, bước đầu tiên chúng ta cần hình thành các câu hỏi. Câu hỏi đầu tiên:

“Chúng ta cần bao nhiêu tiền để sản xuất bộ phim này?”

Câu hỏi này thật sự chưa ổn, nó hơi mơ hồ. Với những người đầu tư phim, họ quan tâm đến lợi nhuận hơn là chi phí. Do đó, câu hỏi nên là:

“Chúng ta thu được bao nhiêu khi sản xuất bộ phim này?”

Câu hỏi này tốt hơn nhiều, nó đã nhắm đến lợi nhuận là tiêu chí các nhà đầu tư đo lường và kiểm tra cơ hội đầu tư. Chúng ta cần suy nghĩ thêm chút, vậy doanh thu sẽ phụ thuộc vào những yếu tố nào? Chúng ta có thể kể ra một loạt các yếu tố sau:

  • Diễn viên có phải các ngôi sao hạng A?
  • Kịch bản hay và nổi tiếng không?
  • Phim có sử dụng các công nghệ tạo hiệu ứng hiện đại nhất?
  • Chi phí quảng bá phim
  • Đạo diễn phim có số má trên thị trường không?

Nhưng chung quy, tất cả các yếu tố này đều quay về chi phí sản xuất hay ngân sách dành cho một bộ phim. Như vậy, chúng ta cần tìm hiểu chi phí sản xuất tất cả các bộ phim đã được phát hành. Các phim như Avatar, Titanic, The Advengers… đã tiêu tốn rất nhiều chi phí, nhưng họ cũng thu lại lợi nhuận khổng lồ. Những thông tin này thì có liên quan gì đến bộ phim mà chúng ta sắp đầu tư? Nếu chúng ta biết được các bộ phim đã tiêu tốn như thế nào để đạt được doanh thu này, chúng ta có thể dự đoán được doanh thu bộ phim của chúng ta thông qua thuật toán Linear Regression – Hồi quy tuyến tính.

Biểu đồ doanh thu ngân sách

Trong bài toán của chúng ta có hai yếu tố:

  • Doanh thu của bộ phim chính là mục tiêu cần tìm ra (target).
  • Ngân sách bộ phim là tham số độc lập (feature).

Như vậy chúng ta đã hoàn thành bước đầu tiên trong dự án, chúng ta đã xác định được các yếu tố (loại dữ liệu cần thiết). Chúng ta sẽ chuyển sang bước tiếp theo thu thập và làm sạch dữ liệu.

Thu thập dữ liệu phim

Trong phần trước, dựa vào các câu hỏi và phân tích chúng ta đã biết cần phải có những dữ liệu về doanh thu và ngân sách các bộ phim. Vậy cần tìm dữ liệu này ở đâu, rất may trong trang web www.the-numbers.com có tất cả dữ liệu này. Tất cả dữ liệu doanh thu và ngân sách trong website này ở dạng bảng và tôi đã đưa chúng vào file csv.

Bạn có thể tải về dữ liệu trong file cost_revenue_dirty.csv để tiếp tục bước tiếp theo làm sạch dữ liệu.

Tiền xử lý dữ liệu

Mở file cost_revenue_dirty.csv và xem nội dung file, có rất nhiều các thông tin bất thường, có những bộ phim có doanh thu bằng 0, ví dụ như phim Singularity chẳng hạn. Nguyên nhân là gì, nếu bạn xem cột Ngày phát hành (Cột B) thì hóa ra phim này chưa ra mắt, đến tận 31/12/2020 mới phát hành.

Dữ liệu doanh thu và ngân sách phim từ the-numbers.com

Thực hiện lọc các phim có doanh thu bằng 0, chúng ta thấy có rất nhiều các bộ phim như vậy. Nhưng không phải tất cả trong số chúng là chưa phát hành mà có những bộ phim đã hoàn thành nhưng không được công chiếu do đang trong quá trình kiện tụng và còn nhiều lý do khác nữa… Như vậy, chúng ta cần loại bỏ tất cả những bộ phim này vì nó là những trường hợp ngoại lệ, nó làm cho dữ liệu của chúng ta thiếu sót.

Chúng ta cũng chỉ cần hai loại thông tin là doanh thu và ngân sách phim do vậy chỉ giữ lại các cột Production Budget ()vàWorldwideGross()vàWorldwideGross(). Các cột thứ hạng phim, ngày phát hành, doanh thu trong nước sẽ được loại bỏ.

Sau khi loại bỏ các thông tin bị mất, sai sót, không đúng định dạng hoặc không cần thiết, chúng ta có được file dữ liệu sau khi làm sạch cost_revenue_clean.csv.

Như vậy, chúng ta đã hoàn thành 2 trong 6 bước đầu tiên là tìm ra các thông tin cần thiết và thu thập, làm sạch dữ liệu. Trong bài tiếp theo chúng ta sẽ tiếp tục bước tiếp theo là khai phá dữ liệu và trực quan hóa dữ liệu thông qua vẽ biểu đồ từ dữ liệu có được.

Tài nguyên liên quan bài viết

Bài viết gốc được đăng tải tại allaravel.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng việc làm CNTT hấp dẫn trên TopDev

Cài đặt Apache Kafka sử dụng Docker Compose

cài đặt apache kafka
Cài đặt Apache Kafka sử dụng Docker Compose

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Trong bài viết trước, mình đã hướng dẫn các bạn cách cài đặt Apache Kafka trên macOS, việc cài đặt sử dụng Docker Compose sẽ giúp chúng ta nhanh chóng start lên một Apache Kafka server mà không tốn nhiều effort, chỉ cần cài đặt Docker và tập tin docker-compose.yml. Cụ thể như thế nào? Trong bài viết này, mình sẽ hướng dẫn các bạn cách cài đặt Apache Kafka sử dụng Docker Compose các bạn nhé!

  Cách thiết lập một dự án Symfony để làm việc với Docker Subdomains

Xem thêm các việc làm OOP lương cao trên TopDev

Mình sẽ tạo mới một tập tin docker-compose.yml với Docker Compose version như sau:

version: '3.8'

Chúng ta sẽ khai báo 2 service, một cho Apache Zookeeper và một cho Apache Kafka:

services:
zookeeper:

kafka:

Vì Apache Kafka muốn chạy được phải có Apache Zookeeper nên mình sẽ khai báo service zookeeper trước.

Chúng ta sẽ sử dụng các Docker Image của Apache Kafka và Apache Zookeeper từ Confluent Platform https://www.confluent.io/ với địa chỉ Docker Hub là https://hub.docker.com/u/confluentinc. Nói nôm na thì Confluent Platform là một mở rộng của Apache Kafka, được xây dựng từ Apache Kafka cùng với các công cụ và service hữu ích khác giúp chúng ta có thể dễ dàng chạy và sử dụng Apache Kafka đó các bạn! Không có Docker Official Image từ Apache Kafka nên chúng ta có thể sử dụng các Docker Images từ Confluent Platform này.

Nội dung của zookeeper service như sau:

zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ports:
- 2181:2181

ZOOKEEPER_CLIENT_PORT là biến môi trường bắt buộc để định nghĩa port mà client có thể kết nối tới Apache Zookeeper. Trong trường hợp của chúng ta là Apache Kafka đó các bạn! Ở đây, mình cũng expose port 2181 là port mặc định của Apache Zookeeper ra ngoài.

Còn kafka service thì có nội dung như sau:

kafka:
image: confluentinc/cp-kafka:latest
depends_on: 
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:29092
ports:
- 29092:29092

KAFKA_ZOOKEEPER_CONNECT được sử dụng để định nghĩa Apache ZooKeeper server mà Apache Kafka sẽ connect tới, còn KAFKA_ADVERTISED_LISTENERS dùng để expose Apache Kafka ra ngoài container để các client có thể connect tới. Đây là những biến môi trường bắt buộc các bạn phải khai báo khi start Docker Container từ cp-kafka Docker Image các bạn nhé!

Chúng ta cũng khai báo networks như sau:

networks:
huongdanjava:
driver: bridge

Toàn bộ nội dung của tập tin docker-compose.yml như sau:

version: '3.8'

services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ports:
- 2181:2181

kafka:
image: confluentinc/cp-kafka:latest
depends_on: 
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:29092
ports:
- 29092:29092

networks:
huongdanjava:
driver: bridge

Nếu bây giờ, các bạn chạy command docker compose up trong thư mục chứa tập tin docker-compose.yml này, các bạn sẽ thấy kết quả như sau:

Bây giờ thì các bạn có thể connect tới Apache Kafka server này để sử dụng rồi.

Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:
Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Producer – Consumer và đồng bộ hóa các luồng trong Java

đồng bộ hóa trong java
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java

Bài viết được sự cho phép của tác giả Giang Phan

Producer/ Consumer là một ví dụ kinh điển về vấn đề đồng hóa các luồng (multi-threading synchronization). Trong bài này tôi sẽ giới thiệu với các bạn vấn đề này và cách giải quyết để giúp các bạn hiểu rõ hơn về Java concurrency và mutli-threading.

Xem thêm các việc làm Java lương cao trên TopDev

Mô tả vấn đề Producer/ Consumer

Vấn đề mô tả hai đối tượng nhà sản xuất (Producer) và người tiêu dùng (Consumer), cả hai cùng chia sẻ một bộ đệm có kích thước cố định được sử dụng như một hàng đợi (queue).

Producer: công việc của nhà sản xuất là tạo dữ liệu, đưa nó vào bộ đệm và bắt đầu lại.

Consumer: công việc người tiêu dùng là tiêu thụ dữ liệu (nghĩa là loại bỏ nó khỏi bộ đệm), từng phần một và xử lý nó. Consumer và Producer hoạt động song song với nhau.

Vấn đề là đảm bảo rằng nhà sản xuất không thể thêm dữ liệu vào bộ đệm nếu nó đầy và người tiêu dùng không thể xóa dữ liệu khỏi bộ đệm trống, đồng thời đảm bảo an toàn cho luồng (thread-safe).

Giải pháp để giải quyết vấn đề Producer/ Consumer

Ý tưởng:

  • Giải pháp cho nhà sản xuất là đi ngủ (wait) nếu bộ đệm đầy. Lần tiếp theo người tiêu dùng xóa một mục khỏi bộ đệm, nó đánh thức (notify) nhà sản xuất, bắt đầu đưa dữ liệu vào bộ đệm.
  • Theo cách tương tự, người tiêu dùng có thể đi ngủ (wait) nếu thấy bộ đệm trống. Lần tiếp theo nhà sản xuất đưa dữ liệu vào bộ đệm, nó đánh thức (notify) người tiêu dùng đang ngủ (wait).
  • Trong khi làm tất cả điều này, phải đảm bảo an toàn cho luồng (thread safe).

Có nhiều giải pháp cho vấn đề Producer Consumer trong Java:

  • Sử dụng synchronized.
  • Sử dụng BlockingQueue.
  • Sử dụng Semaphore.
  • Sử dụng JMS (Java Messaging Service): JMS là một implementation của vấn đề Producer Consumer. Nhiều nhà sản xuất và nhiều người tiêu dùng có thể kết nối với JMS và phân phối công việc. Tôi sẽ giới thiệu với các bạn JMS ở một bài viết khác.

Trong bài này, tôi sẽ giới thiệu với các bạn cách sử dụng BlockingQueue và Semaphore.

Sử dụng BlockingQueue

Để giải quyết vấn đề, chúng ta sẽ cần 3 class:

  • Blocking Queue :
    • Sử dụng synchronized để đảm bảo thread-safe.
    • put() : sử dụng wait() để chờ khi queue đã đầy (full), và notifyAll() để thông báo khi thêm data mới vào queue.
    • take() : sử dụng wait() để chờ khi queue rỗng (empty), và notifyAll() để thông báo khi lấy data ra khỏi queue.
  • Producer Thread(s) : các thread mô phỏng các nhà sản xuất.
  • Consumer Thread(s) : các thread mô phỏng các người tiêu dùng.

Các bạn có thể sử dụng BlockingQueue có sẵn trong package java.util.concurrent . Trong bài này, tôi sẽ tự tạo một BlockingQueue để mô phỏng cơ chế hoạt động của Blocking Queue cho các bạn dễ hiểu.

BlockingQueue.java

package com.gpcoder.producerconsumer;

import java.util.LinkedList;

public class BlockingQueue {
private static final int compacity = 10;
private final LinkedList items = new LinkedList<>();

public synchronized void put(T value) throws InterruptedException {
while (items.size() == compacity) {
System.out.println("Queue is full");
wait();
}
items.addLast(value);
notifyAll();
}

public synchronized T take() throws InterruptedException {
while (items.size() == 0) {
System.out.println("Queue is empty");
wait();
}
notifyAll();
return items.removeFirst();
}

public synchronized int size() {
return items.size();
}
}

Producer.java

package com.gpcoder.producerconsumer;

import java.util.concurrent.ThreadLocalRandom;

public class Producer implements Runnable {

private final BlockingQueue queue;

Producer(BlockingQueue queue) {
this.queue = queue;
}

public void run() {
try {
while (true) {
queue.put(produce());
System.out.println("Produced resource - Queue size() = " + queue.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

private Integer produce() throws InterruptedException {
Thread.sleep(50); // simulate time to produce the data
return ThreadLocalRandom.current().nextInt(1, 100);
}
}

Consumer.java

package com.gpcoder.producerconsumer;

import java.util.concurrent.ThreadLocalRandom;

public class Consumer implements Runnable {

private final BlockingQueue queue;

Consumer(BlockingQueue queue) {
this.queue = queue;
}

public void run() {
try {
while (true) {
queue.take();
System.out.println("Consumed resource - Queue size() = " + queue.size());
Thread.sleep(ThreadLocalRandom.current().nextInt(50, 300)); // simulate time passing
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Main.java

package com.gpcoder.producerconsumer;

public class Main {

public static void main(String[] args) throws InterruptedException {
BlockingQueue boundedBuffer = new BlockingQueue<>();

Producer producer = new Producer(boundedBuffer);
Consumer consumer1 = new Consumer(boundedBuffer);
Consumer consumer2 = new Consumer(boundedBuffer);
Consumer consumer3 = new Consumer(boundedBuffer);

new Thread(producer).start();
new Thread(consumer1).start();
new Thread(consumer2).start();
new Thread(consumer3).start();

Thread.sleep(5000); // After 5s have another comsumer
Consumer consumer4 = new Consumer(boundedBuffer);
new Thread(consumer4).start();
}
}

Output chương trình:

Queue is empty
Queue is empty
Queue is empty
Queue is empty
Produced resource - Queue size() = 0
Consumed resource - Queue size() = 0
Queue is empty
Produced resource - Queue size() = 0
Consumed resource - Queue size() = 0
Produced resource - Queue size() = 1
Consumed resource - Queue size() = 0
Queue is empty
Produced resource - Queue size() = 1
Consumed resource - Queue size() = 0
....
Produced resource - Queue size() = 10
Queue is full
Consumed resource - Queue size() = 9
Produced resource - Queue size() = 10
Queue is full
Consumed resource - Queue size() = 9
Produced resource - Queue size() = 10
Consumed resource - Queue size() = 9
....
Produced resource - Queue size() = 10
Consumed resource - Queue size() = 9
Consumed resource - Queue size() = 8
Produced resource - Queue size() = 9
Consumed resource - Queue size() = 8
...
Consumed resource - Queue size() = 1
Produced resource - Queue size() = 2
Consumed resource - Queue size() = 1
Consumed resource - Queue size() = 0
Queue is empty
Produced resource - Queue size() = 1
Consumed resource - Queue size() = 0
...
  10 lý do cho thấy tại sao bạn nên theo học ngôn ngữ lập trình Java
  10 tips để trở thành Java Developer xịn hơn

Sử dụng Semaphore

Tương tự như BlockingQueue ở trên, với Semaphore chúng ta cũng cần 3 class:

  • Queue : sử dụng 2 Semaphore để đồng bộ hóa dữ liệu.
    • Semaphore Producer: được set giá trị là maximum của buffer size. Giá trị này tương ứng với số lượng item có thể được thêm vào buffer.
    • Semaphore Consumer: được set giá trị là 0. Giá trị này tương ứng với số lượng item có thể được lấy ra khỏi buffer.
  • Producer Thread(s) : các thread mô phỏng các nhà sản xuất.
  • Consumer Thread(s) : các thread mô phỏng các người tiêu dùng.

Ví dụ:

Để dễ theo dõi, tôi sẽ thêm một số dòng log để xem cách hoạt động của Semaphore.

package com.gpcoder.producerconsumer.semaphore;

import java.util.Stack;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadLocalRandom;

public class ProducerConsumerSemaphore {

private static final int BUFFER_SIZE = 4;
private final Semaphore writePermits = new Semaphore(BUFFER_SIZE);
private final Semaphore readPermits = new Semaphore(0);
private final Stack buffer = new Stack<>();

class Producer implements Runnable {
private String name;

public Producer(String name) {
this.name = name;
}

@Override
public void run() {
try {
while (true) {
System.out.println(name + ": acquiring lock...");
System.out.println(name + ": Producer available Semaphore permits now: " + writePermits.availablePermits());
writePermits.acquire();
System.out.println(name + ": got the permit!");

Thread.sleep(50); // simulate time to work
int data = ThreadLocalRandom.current().nextInt(100);
System.out.println(name + ": produced data " + buffer.push(data));

System.out.println(name + ": releasing lock...");
readPermits.release();
System.out.println(name + ": Consumer available Semaphore permits now: " + readPermits.availablePermits());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

class Consumer implements Runnable {
private String name;

public Consumer(String name) {
this.name = name;
}

@Override
public void run() {
try {
while (true) {
System.out.println(name + ": acquiring lock...");
System.out.println(name + ": Consumer available Semaphore permits now: " + readPermits.availablePermits());
readPermits.acquire();

Thread.sleep(ThreadLocalRandom.current().nextInt(50, 300)); // simulate time to work
System.out.println(name + ": consumed data " + buffer.pop());

System.out.println(name + ": releasing lock...");
writePermits.release();
System.out.println(name + ": Producer available Semaphore permits now: " + writePermits.availablePermits());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) throws InterruptedException {
ProducerConsumerSemaphore obj = new ProducerConsumerSemaphore();
Producer producer = obj.new Producer("Producer 1");
new Thread(producer).start();

for (int i = 1; i <= 3; i++) {
Consumer consumer = obj.new Consumer("Consumer " + i);
new Thread(consumer).start();
}

Thread.sleep(5000); // After 5s have another comsumer
Consumer consumer = obj.new Consumer("Consumer " + 4);
new Thread(consumer).start();
}
}

Chạy chương trình trên, ta có kết quả sau:

Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 4
Producer 1: got the permit!
Consumer 1: acquiring lock...
Consumer 1: Consumer available Semaphore permits now: 0
Consumer 2: acquiring lock...
Consumer 2: Consumer available Semaphore permits now: 0
Consumer 3: acquiring lock...
Consumer 3: Consumer available Semaphore permits now: 0
Producer 1: produced data 94
Producer 1: releasing lock...
Producer 1: Consumer available Semaphore permits now: 0
Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 3
Producer 1: got the permit!
Producer 1: produced data 85
Producer 1: releasing lock...
Producer 1: Consumer available Semaphore permits now: 1
Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 2
...
Consumer 2: Producer available Semaphore permits now: 1
Consumer 2: acquiring lock...
Consumer 2: Consumer available Semaphore permits now: 0
Producer 1: produced data 9
Producer 1: releasing lock...
Producer 1: Consumer available Semaphore permits now: 1
Producer 1: acquiring lock...
Producer 1: Producer available Semaphore permits now: 1
Producer 1: got the permit!
...

Tại sao vấn đề Producer/ Consumer lại quan trọng?

Có thể được sử dụng để phân phối công việc giữa các worker khác nhau, dễ dàng tăng hoặc giảm theo yêu cầu. Như bạn thấy trong ví dụ trên, ban đầu một Producer có thể sản xuất đủ cho 3 Consumer. Nhưng sau đó, có thêm một Consumer thì hệ thống sản xuất không đáp ứng được, do đó chúng ta cần tăng hiệu suất của Producer lên hoặc tạo thêm một Producer khác để đủ phục vụ cho Consumer, tránh tình trạng thiếu sản phẩm.

Producer và Consumer được kết nối thông qua BlockingQueue, nó không biết sự hiện diện của nhau, tách rời các mối quan tâm (separation of concern), giúp hệ thống thống có thiết kế tốt hơn, nên dễ dàng nâng cấp và mở rộng.

Producer và Consumer không cần phải có sẵn cùng một lúc. Consumer có thể nhận các nhiệm vụ được sản xuất bởi Producer tại một thời điểm khác nhau.

Tài liệu tham khảo:

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng IT hấp dẫn trên TopDev

Cài đặt Apache Kafka trên macOS

cài đặt apache kafka
Cài đặt Apache Kafka trên macOS

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Trong bài viết này, mình hướng dẫn các bạn cách cài đặt Apache Kafka trong macOS các bạn nhé!

Đầu tiên, các bạn cần đi đến trang Download của Apache Kafka https://kafka.apache.org/downloads để download latest version của nó! Các bạn hãy download phiên bản binary mới nhất.

  Apache Kafka là gì?
  Capacity Planning - Dự toán công suất cho ứng dụng (Tập 1 )

Xem thêm Tuyển dụng Machine Learning Fresher trên TopDev

Sau khi download xong, các bạn hãy giải nén tập tin download này ra.

Để có thể sử dụng Apache Kafka ở bất kỳ đâu trong máy của mình, mình sẽ chỉnh sửa tập tin .bash_profile để thêm đường dẫn tới thư mục bin của Kafka như sau:

export PATH=/Users/khanh/Downloads/kafka_2.13-3.0.0/bin:$PATH

Để những thay đổi của chúng ta có hiệu lực liền, các bạn hãy reload lại tập tin .bash_profile với câu lệnh như sau:

. ./.bash_profile

Để start Apache Kafka, điều đầu tiên, chúng ta cần làm là start Apache ZooKeeper trước.
Các bạn hãy đi đến thư mục của Apache Kafka. Trong thư mục này có thư mục config:

với những tập tin cấu hình mặc định của Apache Kafka và cả Apache ZooKeeper.

Các bạn có thể start Apache ZooKeeper sử dụng command như sau:

zookeeper-server-start.sh config/zookeeper.properties

Kết quả:
Sau khi đã start Apache ZooKeeper rồi thì lúc này, các bạn có thể start Apache Kafka.

Các bạn hãy chạy command sau:

kafka-server-start.sh config/server.properties

Kết quả:
Đến đây thì chúng ta đã hoàn thành việc cài đặt Apache Kafka trên macOS rồi đó các bạn!

Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:
Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Phỏng Vấn Data Analyst: TOP 5 Câu Hỏi Bạn Không Nên Bỏ Qua!

phỏng vấn data analyst
Phỏng Vấn Data Analyst: TOP 5 Câu Hỏi Bạn Không Nên Bỏ Qua!

Để hoàn thành tốt bất cứ quá trình ứng tuyển nào, việc chuẩn bị trước cho các buổi phỏng vấn luôn rất cần thiết nếu muốn có một kết quả thành công. Với vị trí hot như Data Analyst, đâu là những câu hỏi mà ứng viên nên tham khảo trước và có sự chuẩn bị kỹ lưỡng cho mình? Tìm hiểu thêm về top những câu hỏi phỏng vấn Data Analyst mà bạn không nên bỏ qua với bài viết dưới đây nhé!

phỏng vấn business analyst
Top câu hỏi phỏng vấn dành cho Business Analyst

1. Điều gì khiến bạn quyết định trở thành một Data Analyst?

Mục tiêu nghề nghiệp, lý do lựa chọn công việc là một câu hỏi quen thuộc trong hầu hết các buổi phỏng vấn ở mọi ngành nghề. Mục đích của nhà tuyển dụng khi đặt câu hỏi này là để xác định được quyết tâm cũng như mức độ gắn bó của bạn với công việc ra sao, bạn đã có những kinh nghiệm gì và có thể đóng góp được gì cho vị trí mà công ty đang đăng tuyển.

Với một nhà phân tích dữ liệu, câu trả lời của bạn có thể xoay quanh đam mê với việc nghiên cứu những con số, phân tích và đánh giá số liệu chẳng hạn. Đây là một câu hỏi rộng, và không có bất cứ khuôn khổ nào cho nó cả, việc yêu thích số liệu và hứng thú trong việc diễn giải nó hoàn toàn có thể là lý do để bạn lựa chọn gắn bó với công việc. Bạn càng trả lời sáng tạo và hấp dẫn sẽ càng để lại ấn tượng trong mắt người phỏng vấn, cũng như tạo thiện cảm trong suốt buổi phỏng vấn.

2. Trách nhiệm chính của một Data Analyst là gì?

Câu hỏi này sẽ cho nhà tuyển dụng thấy được những hiểu biết căn bản của ứng viên với vị trí Data Analyst. Để có thể kéo dài buổi phỏng vấn, nhà tuyển dụng có hứng thú đào sâu vào kiến thức và kỹ năng của bạn trong buổi phỏng vấn, việc cho thấy kiến thức nền tảng của bạn là rất cần thiết.

  "Làm PM, theo anh không cần biết về code, nhưng phải hiểu về SQL, database, những khái niệm cơ bản của code"
  Big Data có thật sự “toàn năng” và các cơ hội nghề nghiệp trong tương lai?

Với câu hỏi như thế này, bạn có thể tìm hiểu sơ qua về công việc chính mà một Data Analyst sẽ đảm nhận gồm những gì. Đó có thể là việc thu thập, tổng hợp và sắp xếp các dữ liệu. Đánh giá, đưa ra những nhận xét để thúc đẩy hiệu quả và khắc phục các sai sót. Bên cạnh đó thì khả năng phát hiện kịp thời các sai sót cũng là nhiệm vụ mà các Data Analyst cần phải đảm nhận.

câu hỏi phỏng vấn data analyst
Các câu hỏi thường gặp khi phỏng vấn Data Analyst

3. Thách thức lớn nhất bạn đã từng gặp phải trong quá trình làm một Data Analyst là gì?

Câu hỏi này sẽ vừa cho thấy kinh nghiệm làm việc vừa cho thấy khả năng giải quyết vấn đề của các Data Analyst tương lai. Do đó, hãy chắc chắn rằng khi bạn trình bày vấn đề mình đang gặp phải cũng cần đồng thời chia sẻ giải pháp mà bạn đã sử dụng để khắc phục khó khăn đó. Đây là cơ sở rất quan trọng để người phỏng vấn đánh giá năng lực của ứng viên, vậy nên hãy có sự chuẩn bị kỹ càng cho những câu hỏi như thế này.

Tùy theo kinh nghiệm làm việc của mỗi cá nhân mà bạn sẽ nhìn nhận được đâu là vấn đề khó khăn nhất mà mình đang gặp phải. Nên lựa chọn những vấn đề nào có thể đánh giá được khả năng thật sự của bạn cũng như có thể dễ dàng để nhà tuyển dụng hiểu được thay vì các vấn đề có tính chuyên biệt quá cao. Và đừng quên, trình bày cách giải quyết khó khăn thách thức sẽ càng thể hiện rõ năng lực của bạn.

Xem thêm tuyển dụng Data Analytics lương cao trên TopDev

4. Đã có kinh nghiệm làm việc trong lĩnh vực nào và sử dụng công cụ gì để phân tích dữ liệu?

Đây là câu hỏi giúp nhà tuyển dụng đánh giá về kinh nghiệm làm việc trước đây của bạn. Có khá nhiều lĩnh vực hiện nay cần đến sự giúp sức của các Data Analyst như phân tích hoạt động tiếp thị, phân tích tài chính, phân tích hoạt động,… Đừng quên chia sẻ với người phỏng vấn lĩnh vực mà bạn yêu thích và có mong muốn gắn bó để đánh giá sự thích hợp của bạn với công việc mình đang ứng tuyển.

business analyst làm gì

Thông tin về việc bạn đã từng sử dụng qua những công cụ phân tích dữ liệu nào sẽ cho thấy năng lực chuyên môn của bạn. Với câu hỏi này, hãy trình bày với nhà tuyển dụng những phần mềm bạn đã từng sử dụng, đâu là phần mềm bạn cảm thấy yêu thích nhất và thế mạnh của phần mềm đó là gì. Việc sử dụng phần mềm hiệu quả cũng là yếu tố quan trọng để nâng cao hiệu suất làm việc.

5. Khi được giao một dự án phân tích dữ liệu mới, bạn sẽ bắt đầu quy trình làm việc của mình như thế nào?

Câu hỏi này mục đích để đánh giá cách bạn xử lý vấn đề và quy trình làm việc có thật sự đáp ứng được mong đợi của nhà tuyển dụng hay không. Tùy theo cách làm việc của mình mà bạn có thể chia sẻ để nhà tuyển dụng đánh giá. Quan trọng nhất là đảm bảo được tính tối ưu về thời gian, chi phí và đạt được hiệu suất cao nhất cho dự án đó, còn lại sẽ không có bất cứ một quy chuẩn cụ thể nào yêu cầu bạn bắt buộc phải làm như thế cả.

Data Analyst là một công việc đòi hỏi tính kỹ lưỡng và cẩn thận cao. Do đó, trong suốt buổi phỏng vấn Data Analyst hãy cho nhà tuyển dụng thấy được rằng bạn là người đủ cẩn thận và đủ năng lực để đáp ứng những gì họ yêu cầu. Kinh nghiệm và bằng cấp dù quan trọng nhưng sẽ không là yếu tố quyết định tất cả. Đón đọc thêm nhiều bài viết khác về chủ đề Data Analyst tại Topdev.vn/blog nhé!

Có thể bạn quan tâm:

Xem thêm tuyển dụng IT hấp dẫn trên TopDev

Lưu RegisteredClient vào database trong Spring Authorization Server

registeredclient
Lưu RegisteredClient vào database trong Spring Authorization Server

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Trong bài viết trước, mình đã hướng dẫn các bạn cách hiện thực một Authorization Server sử dụng Spring Authorization Server, nhưng thông tin về RegisteredClient trong bài viết này được lưu trong memory. Để lưu thông tin RegisteredClient vào database thì chúng ta sẽ làm như thế nào? Trong bài viết này, mình sẽ hướng dẫn các bạn làm điều này các bạn nhé!

  Bean, ApplicationContext, Spring Bean Life Cycle và Component scan
  Authentication trong Spring Security

Xem thêm các chương trình tuyển dụng Spring lương cao trên TopDev

Đầu tiên, mình cũng tạo mới một Spring Boot project với Web Starter, Security Starter, Data JPA, PostgreSQL Driver:

và Spring Authorization Server:

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.0</version>
</dependency>

để làm ví dụ.

Kết quả:

Mình sẽ cấu hình Spring Security như trong bài viết Hiện thực OAuth Authorization Server sử dụng Spring Authorization Server như sau:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SpringSecurityConfiguration {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
// @formatter:on

return http.build();
}

@Bean
public UserDetailsService users() {
// @formatter:off
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN").build();
// @formatter:on

return new InMemoryUserDetailsManager(user);
}

}


Còn cấu hình cho Authorization Server, mình cũng làm tương tự như bài viết Hiện thực OAuth Authorization Server sử dụng Spring Authorization Server này nhưng phần khai báo thông tin RegisteredClient mình sẽ làm sau:

package com.huongdanjava.springauthorizationserver;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.web.SecurityFilterChain;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;

@Configuration
public class AuthorizationServerConfiguration {

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

return http.formLogin(Customizer.withDefaults()).build();
}

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

@Bean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);

return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

private static RSAKey generateRsa() throws NoSuchAlgorithmException {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

// @formatter:off
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}

private static KeyPair generateRsaKey() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);

return keyPairGenerator.generateKeyPair();
}

@Bean
public ProviderSettings providerSettings() {
// @formatter:off
return ProviderSettings.builder()
.issuer("http://localhost:8080")
.build();
// @formatter:on
}

@Bean
public TokenSettings tokenSettings() {
//@formatter:off
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(30L))
.build();
// @formatter:on
}
}

Để lưu thông tin RegisteredClient vào database, đầu tiên, chúng ta cần định nghĩa database structure để làm việc này.

Mặc định thì Spring Authorization Server cung cấp cho chúng ta script database để tạo database structure. Các bạn có thể copy chúng trong tập tin .jar của Spring Authorization Server:

Các bạn có thể vào Github của Spring Authorization Server ở đây để copy những tập tin này.

Mình sẽ sử dụng Flyway để quản lý database migration:

<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>

bằng cách copy những tập tin schema của Spring Authorization Server vào thư mục src/main/resources/db/migration như sau:

Trong script tạo table oauth2_authorization trong tập tin V1__oauth2-authorization-schema.sql có định nghĩa kiểu dữ liệu BLOB, có lẽ cho Oracle database:

CREATE TABLE oauth2_authorization (
id varchar(100) NOT NULL,
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorization_grant_type varchar(100) NOT NULL,
attributes varchar(4000) DEFAULT NULL,
state varchar(500) DEFAULT NULL,
authorization_code_value blob DEFAULT NULL,
authorization_code_issued_at timestamp DEFAULT NULL,
authorization_code_expires_at timestamp DEFAULT NULL,
authorization_code_metadata varchar(2000) DEFAULT NULL,
access_token_value blob DEFAULT NULL,
access_token_issued_at timestamp DEFAULT NULL,
access_token_expires_at timestamp DEFAULT NULL,
access_token_metadata varchar(2000) DEFAULT NULL,
access_token_type varchar(100) DEFAULT NULL,
access_token_scopes varchar(1000) DEFAULT NULL,
oidc_id_token_value blob DEFAULT NULL,
oidc_id_token_issued_at timestamp DEFAULT NULL,
oidc_id_token_expires_at timestamp DEFAULT NULL,
oidc_id_token_metadata varchar(2000) DEFAULT NULL,
refresh_token_value blob DEFAULT NULL,
refresh_token_issued_at timestamp DEFAULT NULL,
refresh_token_expires_at timestamp DEFAULT NULL,
refresh_token_metadata varchar(2000) DEFAULT NULL,
PRIMARY KEY (id)
);

Nếu các bạn đang sử dụng PostgreSQL database như mình thì cần phải đổi sang kiểu BYTEA các bạn nhé! Không thì chạy database migration sẽ bị lỗi.

Khai báo Datasource để chạy database migration như sau:

spring.datasource.url=jdbc:postgresql://localhost:5432/authorization_server
spring.datasource.username=khanh
spring.datasource.password=1

Giờ thì các bạn có thể định nghĩa RegisteredClient trong database, ví dụ như sau:

@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
// @formatter:off
RegisteredClient registeredClient = RegisteredClient.withId("e4a295f7-0a5f-4cbc-bcd3-d870243d1b05")
.clientId("huongdanjava1")
.clientSecret("123")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.tokenSettings(tokenSettings())
.build();
// @formatter:on

JdbcRegisteredClientRepository registeredClientRepository =
new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(registeredClient);

return registeredClientRepository;
}

Ở đây, mình định nghĩa một RegisteredClient với grant type là client_credentials với ID cố định để mỗi khi start app, không có duplicate record trong database. Tuỳ theo nhu cầu thì các bạn hãy viết code tương ứng nhé!

Chúng ta sẽ sử dụng đối tượng JdbcRegisteredClientRepository để lưu thông tin RegisteredClient này. Tham số khi khởi tạo đối tượng JdbcRegisteredClientRepository là JdbcTemplate.

Lúc này, nếu chạy ứng dụng lên các bạn sẽ thấy trong table oauth2_registered_client, một record mới của RegisteredClient mà mình đã khai báo ở trên được insert vào:

Các bạn cũng nên để ý là client secret được mã hoá sử dụng class DelegatingPasswordEncoder với thuật toán bcrypt. Hiện tại chúng ta chưa thể khai báo thuật toán mà mình muốn!

Xong rồi đó các bạn, nếu bây giờ các bạn chạy ứng dụng và lấy token của clientId ở trên, các bạn sẽ thấy kết quả như sau:

Tải Windows 10 Với Giá $8.19 & Miễn Phí Nâng Cấp Phiên Bản Windows 11

Hệ điều hành Windows 11 vượt trội chính thức ra mắt vào 05/10/2021 là phiên bản nâng cấp hoàn toàn miễn phí cho người dùng hiện tại đang sử dụng Windows 10.

Chỉ cần bạn là người mua Windows 10, bạn có cơ hội lựa chọn nâng cấp lên Windows 11 mà không phải trả thêm bất kỳ chi phí nào. Trong trường hợp máy của bạn chưa được cài đặt Windows 10, Keysoff.com cung cấp những ưu đãi tốt nhất về giải pháp an toàn, không rắc rối và tiết kiệm cho phần mềm Windows 10.

Nhận ưu đãi tốt nhất cho Windows 10 chỉ từ 8.19$

Keysoff.com mong muốn mang đến gói ưu đãi tiết kiệm nhất đối với các sản phẩm phần mềm với mức giá phù hợp để bạn có thể tận hưởng dịch vụ khách hàng đặc biệt cho trải nghiệm mua hàng.

Nâng cấp hệ điều hành Windows nhanh nhất với mức giảm giá ưu đãi 40%

Windows 10 là phiên bản hệ điều hành nhanh nhất và an toàn nhất cho đến thời điểm hiện tại. Bạn đang thiếu rất nhiều tính năng và ưu điểm xử lý giúp máy tính hoạt động hiểu quả hơn khi chưa cài Windows 10 cho máy tính của mình. Bạn có thể sở hữu key win bản quyền Windows 10 chính quyền trong chương trình ưu đãi của Keysoff.com và được giảm giá 40% khi áp dụng mã khuyến mãi “ESL40“.

Hoàn thành nhiều hơn với thời gian ngắn với MS Office cùng mức giảm giá 55%

Bộ phần mềm tối ưu MS Office giúp bạn hoàn thành nhiều công việc trong thời gian ngắn. Keysoff.com đưa ra ưu đãi lớn 55% trong chương trình khuyến mãi mùa thu cho 2 phiên bản MS Office 2016 và 2019 khi áp dụng mã giảm giá “ESL55” khi thanh toán.

Chúng tôi cung cấp nhiều công cụ phần mềm giúp bạn làm việc và học tập trên máy tính thoải mái hơn, an toàn hơn với mức giá ưu đãi cho toàn bộ sản phẩm và giảm giá tới 90% trong dịp khuyến mãi mùa hè với phiên bản tải về chính thức và dễ dàng cài đặt. Đừng bỏ lỡ dịp khuyến mãi lớn này.

Refactoring Design Pattern với tính năng mới trong Java 8

refactoring design pattern
Refactoring Design Pattern với tính năng mới trong Java 8

Bài viết được sự cho phép của tác giả Giang Phan

Trong bài này, tôi sẽ giới thiệu với các bạn cách sử dụng một số tính năng mới trong Java 8 như Lambda , Function, Supplier, … để refactor code của một số Design Pattern.

Xem thêm nhiều việc làm Java lương cao trên TopDev

Refactoring Strategy Design Pattern

Strategy.java

package com.gpcoder.designpatterns.strategy;

public interface Strategy {
void performTask();
}

Strategy Pattern không sử dụng Lambda

StartegyPatternExample.java

package com.gpcoder.designpatterns.strategy;

import java.util.Arrays;
import java.util.List;

class EagerStrategy implements Strategy {

@Override
public void performTask() {
System.out.println("Eager strategy");
}
}

class LazyStratgey implements Strategy {

@Override
public void performTask() {
System.out.println("Lazy strategy");
}
}

public class StartegyPatternExample {
public static void main(String[] args) {
Strategy eagerStrategy = new EagerStrategy();
Strategy lazyStrategy = new LazyStratgey();
List strategies = Arrays.asList(eagerStrategy, lazyStrategy);
for (Strategy stg : strategies) {
stg.performTask();
}
}
}

Strategy Pattern sử dụng Lambda

package com.gpcoder.designpatterns.strategy;

import java.util.Arrays;
import java.util.List;

public class LambdaStartegyPatternExample {

public static void main(String[] args) {
Strategy eagerStrategy = () -> System.out.println("Eager strategy");
Strategy lazyStrategy = () -> System.out.println("Lazy strategy");
List strategies = Arrays.asList(eagerStrategy, lazyStrategy);
strategies.forEach((elem) -> elem.performTask());
}
}

Như bạn thấy, sử dụng Lambda code chúng ta đơn giản hơn nhiều, không cần tạo thêm các class.

  30 tiện ích Chrome (extensions) cho Designer và Developer
  30 tiện ích Chrome cho designer và dev

Refactoring Observer Design Pattern

Observer.java

package com.gpcoder.designpatterns.observer;

public interface Observer {
void update(String str);
}

Subject.java

package com.gpcoder.designpatterns.observer;

public interface Subject {

void registerObserver(Observer observer);

void notifyObservers(String str);
}

AccountService.java

package com.gpcoder.designpatterns.observer;

import java.util.ArrayList;
import java.util.List;

public class AccountService implements Subject {

private final List observers = new ArrayList<>();

public void login(String username) {
System.out.println("Login: " + username);
notifyObservers(username);
}

@Override
public void registerObserver(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}

@Override
public void notifyObservers(String str) {
for (Observer observer : observers) {
observer.update(str);
}
}
}

Observer Pattern không sử dụng Lambda

package com.gpcoder.designpatterns.observer;

class Logger implements Observer {
@Override
public void update(String str) {
System.out.println("Logger: " + str);
}
}

class Mailer implements Observer {
@Override
public void update(String str) {
System.out.println("Mailer: " + str);
}
}

public class ObserverPatternExample {
public static void main(String[] args) {
AccountService account = new AccountService();
// Register Observers
account.registerObserver(new Logger());
account.registerObserver(new Mailer());
// Call service
account.login("gpcoder");
}
}

Observer Pattern sử dụng Lambda

package com.gpcoder.designpatterns.observer;

public class LambdaObserverPatternExample {
public static void main(String[] args) {
AccountService account = new AccountService();
// Register Observers
account.registerObserver(str -> System.out.println("Logger: " + str));
account.registerObserver(str -> System.out.println("Mailer: " + str));
// Call service
account.login("gpcoder");
}
}

Chạy 2 chương trình trên, ta có cùng kết quả:

Login: gpcoder
Logger: gpcoder
Mailer: gpcoder

Refactoring Chain of Responsibility Pattern

Filter.java

package com.gpcoder.designpatterns.chain;

public abstract class Filter {

private Filter nextFilter;

public String doFilter(String str) {
String result = handleString(str);
if (nextFilter != null) {
return nextFilter.doFilter(result);
}
return result;
}

public void setNextFilter(Filter nextFilter) {
this.nextFilter = nextFilter;
}

protected abstract String handleString(String str);
}

Chain of Responsibility Pattern không sử dụng Lambda

package com.gpcoder.designpatterns.chain;

class Filter1 extends Filter {
@Override
protected String handleString(String str) {
System.out.println("Filter1: " + str);
return str + "->Filter1";
}
}

class Filter2 extends Filter {
@Override
protected String handleString(String str) {
System.out.println("Filter2: " + str);
return str + "->Filter2";
}
}

class Filter3 extends Filter {
@Override
protected String handleString(String str) {
System.out.println("Filter3: " + str);
return str + "->Filter3";
}
}

class AppFilter {
public static Filter getFilter() {
Filter1 filter1 = new Filter1();
Filter2 filter2 = new Filter2();
Filter3 filter3 = new Filter3();
filter1.setNextFilter(filter2);
filter2.setNextFilter(filter3);
return filter1;
}
}

public class ChainOfResponsibilityExample {

public static void main(String[] args) {
// Build the chain of responsibility
Filter filter = AppFilter.getFilter();
// Execute filter
String result = filter.doFilter("gpcoder");
System.out.println("Final data: " + result);
}
}

Chain of Responsibility Pattern sử dụng Lambda và Function

package com.gpcoder.designpatterns.chain;

import java.util.function.Function;
import java.util.function.UnaryOperator;

public class LamdaChainOfResponsibilityExample {

public static void main(String[] args) {

UnaryOperator filter1 = (str) -> {
System.out.println("Filter1: " + str);
return str + "->Filter1";
};

UnaryOperator filter2 = (str) -> {
System.out.println("Filter2: " + str);
return str + "->Filter2";
};

UnaryOperator filter3 = (str) -> {
System.out.println("Filter3: " + str);
return str + "->Filter3";
};

// Compose all functions resulting in a chain of operations.
Function<String, String> appFilter = filter1.andThen(filter2).andThen(filter3);
String result = appFilter.apply("gpcoder");
System.out.println("Final data: " + result);
}
}

Lưu ý: UnaryOperator là một Function, có cùng kiểu dữ liệu đầu vào và đầu ra. UnaryOperator<String> tương đương với cách viết Function<String, String>.

Chạy 2 chương trình trên, chúng ta có cùng kết quả:

Filter1: gpcoder
Filter2: gpcoder->Filter1
Filter3: gpcoder->Filter1->Filter2
Final data: gpcoder->Filter1->Filter2->Filter3

Refactoring Factory Method Design Pattern

Bank.java

package com.gpcoder.designpatterns.factory;

public interface Bank {
String getBankName();
}

TPBank.java

package com.gpcoder.designpatterns.factory;

public class TPBank implements Bank {
@Override
public String getBankName() {
return "TPBank";
}
}

VietcomBank.java

package com.gpcoder.designpatterns.factory;

public class VietcomBank implements Bank {
@Override
public String getBankName() {
return "VietcomBank";
}
}

BankType.java

package com.gpcoder.designpatterns.factory;

public enum BankType {
VIETCOMBANK, TPBANK;
}

Factory Method Pattern không sử dụng Java 8

package com.gpcoder.designpatterns.factory;

class BankFactory {
public static final Bank getBank(BankType bankType) {
switch (bankType) {
case TPBANK:
return new TPBank();
case VIETCOMBANK:
return new VietcomBank();
default:
throw new IllegalArgumentException("This bank type is unsupported");
}
}
}

public class FactoryMethodExample {
public static void main(String[] args) {
Bank bank = BankFactory.getBank(BankType.TPBANK);
System.out.println(bank.getBankName()); // TPBank
}
}

Factory Method Pattern sử dụng Supplier và Method Reference

package com.gpcoder.designpatterns.factory;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

class Java8BankFactory {

private final static Map<BankType, Supplier> map = new HashMap<>();

static {
map.put(BankType.TPBANK, TPBank::new);
map.put(BankType.VIETCOMBANK, VietcomBank::new);
}

public static final Bank getBank(BankType bankType) {
Supplier bank = map.get(bankType);
if (bank == null) {
throw new IllegalArgumentException("This bank type is unsupported");
}
return bank.get();
}
}

public class Java8FactoryMethodExample {
public static void main(String[] args) {
Bank bank = Java8BankFactory.getBank(BankType.TPBANK);
System.out.println(bank.getBankName()); // TPBank
}
}

Java 8 mang đến cho chúng ta rất nhiều tiện ích, các bạn hãy thử refactor code của mình sang Java 8 để code được gọn ràng hơn.

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng công nghệ thông tin hấp dẫn trên TopDev

Sử dụng Flyway trong Spring Boot

flyway
Sử dụng Flyway trong Spring Boot

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Mình đã giới thiệu với các bạn về Flyway để hiện thực database migration. Đối với các ứng dụng Spring Boot muốn sử dụng Flyway thì việc integrate này rất dễ dàng vì Spring Boot đã hỗ trợ. Cụ thể như thế nào? Chúng ta hãy cùng nhau tìm hiểu trong bài viết này các bạn nhé!

  Cách làm một ứng dụng Chat cho Android & iOS bằng Contus Fly như thế nào?

Xem thêm việc làm Spring Boot lương cao trên TopDev

Đầu tiên, mình sẽ tạo mới một Spring Boot project với Flyway, Spring Data JDBC và PostgreSQL dependencies như sau:

Kết quả:

Nếu kiểm tra thư mục src/main/resources trong project vừa tạo, các bạn sẽ thấy có một thư mục là db/migration. Đây chính là thư mục mặc định mà chúng ta sẽ chứa các tập tin SQL để Spring Boot với Flyway làm database migration đó các bạn!

Bây giờ, để làm ví dụ, mình sẽ tạo mới một tập tin SQL V2021.10.20.00000__Init.sql nằm trong thư mục src/main/resources/db/migration:

để tạo mới table student như sau:

CREATE TABLE student (
ID INT PRIMARY KEY,
name VARCHAR(250) NOT NULL
);

Việc tiếp theo chúng ta cần làm là cấu hình Datasource trong tập tin application.properties, ví dụ của mình như sau:

spring.datasource.url=jdbc:postgresql://localhost:5432/flyway_example
spring.datasource.username=khanh
spring.datasource.password=1

với flyway_example là tên database ví dụ của mình.

Bây giờ, nếu các bạn chạy ứng dụng và truy cập đến database trên, các bạn sẽ thấy kết quả như sau:

Table flyway_schema_history và cả table student mà chúng ta định nghĩa trong tập tin SQL đã được tạo.

Các bạn có thể thấy, việc sử dụng Flyway trong ứng dụng Spring Boot rất đơn giản, không tốn nhiều effort phải không các bạn?

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng công nghệ hấp dẫn trên TopDev

ServletContextEvent và ServletContextListener trong Jakarta EE Servlet

jakarta ee servlet
ServletContextEvent và ServletContextListener trong Jakarta EE Servlet

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

ServletContextEvent và ServletContextListener là 2 đối tượng của Jakarta EE Servlet có nhiệm vụ ghi nhận và xử lý những thay đổi trong ServletContext của một ứng dụng web khi ứng dụng web này được deploy lên Server Runtime. Bất kỳ những thay đổi nào của ServletContext, ServletContextEvent cũng sẽ ghi nhận và ServletContextListener cũng sẽ thực hiện những thay đổi đó.

  Nói về ServletContext và ServletConfig trong Jakarta EE Servlet
  Định nghĩa request URL trong ứng dụng Jakarta EE RESTful Web Services

Xem thêm tuyển dụng Scala hấp dẫn trên TopDev

Những thay đổi này sẽ xảy ra lúc ứng dụng được deploy và lúc chúng ta stop ứng dụng. Các bạn có thể thêm code để khi ứng dụng chạy lên hoặc stop đi, một số thao tác sẽ được thực hiện tuỳ theo nhu cầu của mình. Để làm được điều này, các bạn hãy tạo mới một custom class implement ServletContextListener interface như sau:

package com.huongdanjava.jakartaee;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;

public class HuongDanJavaServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
}

Sau đó thì khai báo class custom này vào tập tin web.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">

<listener>
<listener-class>com.huongdanjava.jakartaee.HuongDanJavaServletContextListener</listener-class>
</listener>
</web-app>

Hoặc các bạn cũng có thể sử dụng annotation @WebListener với custom class trên:

package com.huongdanjava.jakartaee;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

@WebListener
public class HuongDanJavaServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
}

Một trong 2 cách thôi các bạn nhé!

Lúc này, khi chạy ứng dụng, các bạn sẽ thấy code bên trong phương thức contextInitialized() được gọi như sau:

Nếu bạn stop Server Runtime, các bạn sẽ thấy code trong phương thức contextDestroyed() được gọi:

Những Thông Tin Cần Biết Về Vị Trí ICT Business Analyst

ict business analyst
Những Thông Tin Cần Biết Về Vị Trí ICT Business Analyst

Đi cùng với sự phát triển của thời đại công nghệ, vị trí Business Analyst cũng nổi lên như một yếu tố then chốt trong việc nghiên cứu và tìm hiểu giá trị thực sự của một doanh nghiệp. Trong nhóm ngành BA, ICT Business Analyst dường như vẫn là cái tên xa lạ với nhiều người. Vậy cụ thể thì công việc này là gì, cần có những kỹ năng và chuyên môn ra sao để đảm nhận chúng? Cùng TopDev tìm hiểu thêm về vị trí này với bài viết dưới đây.

ict business analyst
ICT Business Analyst và những điều cần biết

ICT Business Analyst là gì?

Với vị trí chuyên môn tương đồng với các Business Analyst khác, công việc của ICT Business Analyst cũng là phân tích các nghiệp vụ kinh doanh nhưng tập trung nhiều hơn vào các yếu tố liên quan đến phần mềm và truyền thông – Information and Communication Technology. Theo đó, các ICT BA sẽ tận dụng kiến thức của mình để xây dựng hệ thống thông qua việc phân tích và lên kế hoạch phát triển công nghệ cũng như đẩy mạnh các yếu tố truyền thông. Ngách làm việc chính của ICT BA là với các System Analysts.

Theo tài liệu của một số bên liên quan thì ICT BA sẽ sử dụng kỹ thuật mô hình hóa dữ liệu và quy trình để thiết lập nên một hệ thống cụ thể cho việc thiết kế cũng như phát triển phần mềm được trơn tru và hiệu quả hơn. Do đặc thù của vị trí này không chỉ liên quan đến phân tích nghiệp vụ mà còn cả công nghệ thông tin và truyền thông nên các ứng viên tốt nghiệp từ những chuyên ngành này cũng là ứng viên sáng giá cho vị trí ICT Business Analyst.

Công việc của một ICT Business Analyst

Công việc chính của một ICT BA sẽ là phụ trách trao đổi thông tin với người dùng cuối để nắm được các vấn đề mà họ đang gặp phải, tổng hợp thành các tài liệu nghiệp vụ cần thiết cho công việc.

  • Bằng các phương thức nghiệp vụ, các ICT BA phải xác định và đánh giá đúng tình trạng vấn đề mà công ty đang gặp phải để đưa ra hướng giải quyết thích hợp, giảm thiểu tối đa các sai sót có thể xảy ra cũng như nâng cao hiệu suất công việc.
  • Sử dụng các phương pháp nghiệp vụ để quản lý dự án đảm bảo đạt được kết quả tối đa, phát triển kế hoạch dự án với chi phí và nguồn lực hợp lý nhất.
  Business Analyst Cần Học Gì Để Trở Thành Chuyên Gia Trong Ngành?
  Công Việc Của Business Analyst Và Tất Tần Tật Các Thông Tin Cần Biết Về Nghề BA
  • Chịu trách nhiệm triển khai các dự án trên nhiều nền tảng với nhiều giải pháp khác nhau để kiểm thử chất lượng hệ thống và đảm bảo tính toàn vẹn ở mức cao nhất.
  • Triển khai chạy thử các giải pháp mới để tối ưu hiệu quả cho việc phát triển sản phẩm cũng như nâng cao trải nghiệm của người dùng.
  • Nghiên cứu và lên phương án cho các tài liệu để giới thiệu đến người dùng cũng như tiến hành các khóa đào tạo chính thức nếu cần thiết.
  • Chịu trách nhiệm chính trong việc đưa ra các tư vấn chính xác liên quan đến kỹ thuật và hỗ trợ truyền thông cho các giải pháp mới hoặc tối ưu các phương án ở thời điểm hiện tại.

Với những nhiệm vụ và các vai trò như thế, có thể khái quát sản phẩm cụ thể của một ICT Business Analyst là cho ra các quy trình nghiệp vụ đã được mô hình hóa và cung cấp các giải pháp cho hệ thống cũ, mới.

Học gì để trở thành ICT Business Analyst?

Để trở thành một ICT BA, việc lựa chọn các ngành học liên quan đến khoa học máy tính, công nghệ, phần mềm và hệ thống thông tin kinh tế sẽ giúp ích rất nhiều cho công việc của bạn sau này.

công việc ict business analyst
ICT Business Analyst học gì?

Cụ thể, trong quá trình học bạn sẽ được tiếp xúc với các kiến thức về kỹ thuật có thể áp dụng trong việc phân tích hệ thống. Các kiến thức về ứng dụng phần mềm, phần cứng, nghiệp vụ ICT và các giải pháp.

Bên cạnh đó, các kỹ năng mềm như kỹ năng quản lý dự án, kỹ năng làm việc nhóm, giải quyết vấn đề và kỹ năng đàm phán, thuyết phục là những điều bạn nên tìm hiểu qua nếu muốn công việc của mình diễn ra thuận lợi nhất sau này.

Xem thêm các việc làm Business Analyst lương cao trên TopDev

ICT Business Analyst là một ngành nghề mới, do đó để có thể theo đuổi ngành này một cách lâu dài và chuyên nghiệp nhất, bạn cần có sự nghiên cứu kỹ lưỡng và đầu tư thật sự cho nó. Hi vọng bài viết này sẽ giúp bạn tìm kiếm được một số thông tin cần thiết. Đón đọc thêm nhiều chủ đề hấp dẫn khác tại Topdev.vn/blog nhé!

Có thể bạn quan tâm:

Xem thêm công việc CNTT hấp dẫn trên TopDev

Appota tổ chức Webinar: “Giải Mã Nhân Lực Ngành IT”

Song hành với sự phát triển của công nghệ thì lĩnh vực IT cũng đang nhận được sự quan tâm rất lớn từ thị trường tuyển dụng. Chương trình: “Giải mã nhân lực ngành IT” diễn ra vào 15h ngày 22/10 sẽ đem đến cho các bạn trẻ, đặc biệt là các bạn sinh viên những góc nhìn thực tế về lĩnh vực này.

Dựa trên Báo cáo về thị trường IT Việt Nam 2021 của TopDev, đến năm 2022 Việt Nam sẽ còn cần đến 530.000 nhân lực trong ngành công nghệ thông tin. Tuy nhiên nguồn cung nhân lực sẽ chỉ đáp ứng được 72%. Thực tế là, số lượng ngành học về công nghệ thông tin ở các trường đại học đang mở rộng ngày càng nhiều cũng như số lượng cử nhân tốt nghiệp chuyên ngành này vẫn tăng cao qua mỗi năm, tại sao vẫn có sự chênh lệch này? Sự thiếu hụt này chủ yếu là do trình độ của lập trình viên và yêu cầu doanh nghiệp đặt ra vẫn chưa thực sự cân bằng với nhau. Trong số hơn 55.000 sinh viên công nghệ thông tin tốt nghiệp mỗi năm chỉ có khoảng 16.500 sinh viên (38%) đáp ứng được những kỹ năng và chuyên môn mà doanh nghiệp cần.

Vậy đâu là nguyên nhân dẫn đến tình trạng này, Webinar sẽ diễn ra dưới hình thức trực tuyến, phần nào đem đến câu trả lời và những nhận định từ các chuyên gia hàng đầu trong ngành với sự tham gia của: Anh Nguyễn Văn Vũ – CTO Appota, Anh Đặng Minh Tuấn – Head of PTIT Blockchain Lab, Tác giả phần mềm Vietkey, Anh Lê Ngọc Tuấn –  Founder Maker HaNoi, Anh Đặng Thái Sơn – CMO Appota (host).

Đầu tiên là khách mời – TS Đặng Minh Tuấn là tác giả của Vietkey, phần mềm gõ tiếng Việt đầu tiên trước khi UNICODE chính thức áp dụng. Nguyên là Đại tá, Phó Giám đốc Phòng thí nghiệm trọng điểm Quốc gia về An toàn thông tin, anh hiện đang giữ chức vụ Viện trưởng Viện Nghiên cứu Ứng dụng CMC; Trưởng phòng Lab Blockchain – Học viện Công nghệ Bưu chính Viễn thông; Phó Chủ tịch Câu lạc bộ FinTech của Hiệp hội Ngân hàng Việt Nam. Là Tiến sĩ chuyên ngành Toán và Mật mã, và nghiên cứu Blockchain từ 2014, anh Tuấn sở hữu nhiều kinh nghiệm về nhiều lĩnh vực Blockchain như Cryptography, Smart Contract, Consensus algorithms…

Anh Đặng Minh Tuấn – Head of PTIT Blockchain Lab, Tác giả phần mềm Vietkey

Tiếp đến, khách mời –  Anh Lê ngọc Tuấn là Founder Maker Hà Nội, là một chuyên gia về Robotic với 12 năm kinh nghiệm về lập trình nhúng, robot và các thiết bị điều khiển tự động. Ngoài ra, anh còn là thành viên Ban điều hành Mạng lưới khởi nghiệp IoT Việt Nam. Hiện tại anh đang công tác tại VinAI. Maker Hà Nội là cộng đồng những nhà sáng chế trong lĩnh vực công nghệ trên địa bàn Hà Nội nổi tiếng với các sản phẩm IoT và ứng dụng thực tiễn. Tổ chức này không chỉ phát triển thành công nhiều dự án IoT mà còn góp phần thúc đẩy tinh thần sáng tạo cho thế hệ trẻ thông qua hàng chục khóa đào tạo về phát triển robotic, sản phẩm nhà thông minh cho hàng trăm học sinh các cấp. Đặc biệt Maker Hanoi còn xây dựng các mã nguồn mở về IoT và trí tuệ nhân tạo cho cộng đồng các bạn sinh viên Việt Nam.

Anh Lê Ngọc Tuấn – Founder Maker HaNoi

Khách mời thứ 3, anh Nguyễn Văn Vũ – CTO của Appota. Với gần 15 năm kinh nghiệm trong ngành CNTT,  anh Vũ trực tiếp tham gia xây dựng và phát triển các sản phẩm, dự án ở nhiều lĩnh vực công nghệ như: Fintech; Saas; Blockchain, IOT… Các dự án tiêu biểu như: Giải pháp quản trị nhân sự ACheckin (dự án được vinh danh tại giải Sao Khuê 2021), Ví điện tử Appota, Dự án smarthome AppotaHome… Anh có vai trò quan trọng trong việc đưa Appota trở thành một trong những công ty tiên phong xây dựng hệ sinh thái sản phẩm, dịch vụ cho người dùng di động, đồng thời là một trong số đơn vị phát hành game lớn nhất tại Việt Nam.

Anh Nguyễn Văn Vũ – CTO Appota

Dẫn dắt chương trình – Anh Đặng Thái Sơn – Hiện nay đang là CMO của Appota đồng thời là Giám đốc dự án Appota Esport.  Trong 8 năm gắn bó cùng Appota, anh đã tham gia vào nhiều dự án khởi nghiệp, nghiên cứu thị trường, tổ chức sự kiện, chương trình hội thảo và hoạt động cộng đồng nhằm kết nối các startup Việt Nam và các đối tác nước ngoài. Anh cũng là người truyền cảm hứng, tạo dựng thương hiệu cho các sản phẩm công nghệ của Appota, đưa các sản phẩm đến gần hơn với người dùng. Trong chương trình, anh Sơn đóng vai trò (host), người dẫn dắt và kết nối các khách mời.

Anh Đặng Thái Sơn – CMO Appota

Với kinh nghiệm 10 năm hoạt động trong lĩnh vực Công nghệ, Appota luôn khao khát được truyền cảm hứng cho cộng đồng và mang đến nhiều kiến thức cho các bạn trẻ thông qua sự kiện lần này. Webinar: “Giải mã nhân lực ngành IT” chắc chắn sẽ cung cấp những thông tin, kiến thức thực tế, giúp các bạn trẻ hiểu rõ hơn về xu hướng cũng như kỹ năng cần thiết để phát triển trong ngành công nghệ. Bên cạnh đó, các bạn cũng sẽ được lắng nghe những chia sẻ về ngành CNTT từ các chuyên gia hàng đầu, giúp các bạn có những định hướng đúng đắn trong việc chọn môi trường phát triển phù hợp. Ngoài ra, chương trình cũng mang đến cơ hội tiếp xúc giữa doanh nghiệp và  ứng viên từ đó mở rộng cơ hội nghề nghiệp cho các bạn trẻ, cũng như đem đến nguồn nhân lực tiềm năng và ưu tú cho các đơn vị.

Đăng ký chương trình và gửi câu hỏi bạn cho chương trình và các diễn giả Tại đây: https://webinar.appota.com/

Những bước cơ bản cần làm ngay sau khi cài đặt WordPress

cài đặt wordpress
Những bước cơ bản cần làm ngay sau khi cài đặt WordPress

Bài viết được sự cho phép của blogchiasekienthuc.com

Chào các bạn, trong bài các bài viết hôm trước thì mình đã cùng các bạn cài đặt thành công WordPress và chạy một trang web WordPress thông qua server Xampp một cách khá là đơn giản rồi.

Vậy nên trong bài viết tiếp theo này, mình sẽ cùng các bạn thực hiện nốt một số thao tác cơ bản sau khi cài đặt.

Các thao tác này liên quan đến việc sử dụng theme, đăng bài viết và chỉnh sửa thông tin tài khoản quản trị. Okay, giờ thì bắt đầu thôi nhỉ !

  50 keywords mà mọi JAVA developer nên biết
  11 cách tăng tốc nhanh cho WordPress bằng file wp-conig.php

Xem thêm việc làm WordPress lương cao trên TopDev

#1. Khởi chạy server và truy cập vào web quản trị

Ban đầu, để có thể truy cập vào trang web người dùng, cũng như trang web quản trị thì các bạn lưu ý là phải mở Xampp Server và chạy cho mình hai module sau:

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (1)

Sau đó, để truy cập vào trang quản trị như hình bên dưới thì các bạn sẽ phải nhập thông tin đăng nhập bao gồm:

  1. Username hoặc Email mà các bạn đã đăng ký tài khoản.
  2. Mật khẩu của tài khoản đó.

Những thông tin này thì các bạn phải nhớ nhé, vì chúng ta sẽ phải sử dụng rất thường xuyên. Bạn nên lưu lại từ bước cài đặt trong quá trình cài đặt mà mình đã hướng dẫn ở bài viết trước.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (2)

#2. Thay đổi và tùy chỉnh theme (giao diện) cho trang web

Sau khi truy cập được vào trang web quản trị như bên dưới thì ở tab Appearance => các bạn chọn mục Theme.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (3)

Bên dưới là giao diện chính để các bạn thực hiện các thao tác quản lý theme cho trang web. Tại đây các bạn có thể:

  1. Tìm kiếm theme
  2. Biết được các theme đã được cài đặt
  3. Biết được theme nào đang được sử dụng cho trang web
  4. Cài đặt theme bằng cách bấm vào Upload Theme

Cụ thể các bạn xem hình bên dưới để dễ hình dung hơn. Thông thường các theme có sẵn trên WordPress là các theme miễn phí, tính năng hạn chế, nhưng bạn có thể sử dụng tạm để làm quen với WordPress cũng được.

Còn khi đã quen rồi và bạn quyết định đưa website của bạn lên Internet thì bạn có thể lên các chợ theme lớn như Themeforest, MOJO Marketplace, Mythemeshop, Elegant Themes, StudioPress…. để mua và sử dụng.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (4)

WordPress hỗ trợ hai hình thức cài đặt theme, đó là:

  • Bạn sẽ tìm kiếm trong danh sách các theme miễn phí có trên WordPress và tiến hành cài đặt như một Plugin.
  • Bạn có thể mua/ tải theme về máy tính => sau đó upload lên để cài đặt (đối với các theme trả phí bạn sẽ phải sử dụng phương pháp này).

——–// phương pháp cài theme trực tiếp //———

Với cách đầu tiên thì như hình bên dưới, các bạn có thể tìm một theme nào đó => rồi bấm Preview để xem trước theme đó như thế nào, hoặc bấm Install để cài đặt ngay lập tức.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (5)

Ví dụ như ở đây mình bấm Preview để xem trước thì lúc này trang web sẽ được chuyển hướng sang một trang khác để mình có thể xem trước giao diện theme đó sẽ như thế nào.

=> Nếu các bạn thấy theme đó phù hợp thì có thể bấm Install để tiến hành cài đặt.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (6)

Sau khi cài đặt xong, các bạn quay lại trang ban đầu. Tại đây, các bạn bấm Active để theme đó chính thức được sử dụng cho trang web của mình.

Lưu ý: Theme nào đang được sử dụng để hiển thị cho trang web sẽ có chữ Actived nha các bạn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (7)

——–// phương pháp cài theme bằng phương pháp upload //———

Nhưng như mình đã nói bên trên rồi đó, thông thường các theme miễn phí của WordPress không đáp ứng được hết các nhu cầu sử dụng của người dùng.

Chính vì vậy chúng ta thường lựa chọn mua theme => rồi sau đó sử dụng tính năng Upload Theme để cài đặt theme đó cho trang web.

Để có thể tải và cài đặt theme thì các bạn có xem hình bên dưới. Theme cho WordPress thường sẽ được nén dưới dạng file *.zip. Các bạn chỉ cần tải theme lên rồi cài đặt là được.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (8)

Sau khi tải lên thì các bạn bấm Install Now để WordPress bắt đầu cài đặt.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (9)

Okay, quá trình cài đặt theme diễn ra nhanh hay chậm sẽ tùy thuộc vào dung lượng của theme các bạn tải lên. Theme càng nặng thì quá trình cài đặt có thể sẽ lâu hơn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (10)

Sau khi cài đặt nếu các bạn muốn sử dụng theme đó cho trang web thì nhớ là phải bấm Active như hình bên dưới nhé.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (11)

#3. Tạo bài viết mới trên WordPress

Tạo bài viết mới là một trong những thao tác mà chúng ta thường xuyên phải làm. Để có thể tạo được bài viết mới thì các bạn chọn vào tab Posts như hình bên dưới.

Ở đây các bạn có thể:

  • Thêm mới bài viết bằng cách bấm vào Add New
  • Tìm kiếm bài viết
  • Xem danh sách các bài viết cũng như một số công cụ filter.

Cụ thể các bạn có thể xem ảnh bên dưới để dễ hình dung hơn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (12)

Tiếp theo, để tạo bài viết mới thì các bạn bấm vào Add New. Phiên bản WordPress mình cài có thể sẽ hơi khác một chút so với các phiên bản trước ở phần này.

Cụ thể là ở các phiên bản trước, khi chúng ta tạo mới một bài viết thì sẽ không chuyển qua trang mới, nhưng ở phiên bản mới (phiên bản 5.8.1) thì nó sẽ chuyển qua một tab mới như bên dưới.

NOTE: Đối với những người dùng WordPress lâu năm, đã quá quen thuộc với giao diện WordPress cũ thì mọi người sẽ thường sẽ chuyển về giao diện soạn thảo cũ cho dễ làm.

Nhưng với những bạn mới làm quen với WordPress thì mình khuyên các bạn nên sử dụng giao diện soạn thảo mới này của WordPress. Bởi giao diện mới này sẽ có nhiều ưu điểm nổi bật hơn.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (13)

Sau khi hoàn thành bài viết, các bạn có thể bấm vào phần Preview để xem trước nội dung bài viết khi hiển thị lên web sẽ như thế nào.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (14)

Hình bên dưới chính là bài viết mình đang xem ở chế độ Preview (xem trước)..

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (15)

Nếu các bạn thấy bài viết ổn rồi thì hãy quay lại trang trước => và bấm Publish để xuất bản bài viết.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (16)

Vậy là bài viết được đã được xuất bản ở chế độ Public (công khai), các bạn có thể lên web để kiểm tra và đọc bài viết mà bạn vừa xuất bản.

Bạn có thể bấm vào nút View Post hoặc cũng có thể bấm Copy Link để sao chép lấy link trực tiếp của bài viết.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (17)

Quay lại màn hình chính, các bạn sẽ thấy bài viết vừa rồi đã được thêm vào danh sách. Ở đây các bạn hoàn toàn có thể bấm Edit để chỉnh sửa bài viết, hoặc Trash để xóa bài viết đó.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (18)

Nhưng tất nhiên, bài viết này mình chỉ giới thiệu cho các bạn biết cách để viết một bài viết mới và xuất bản bài viết. Còn bạn chưa nên bắt tay vào viết ngay nhé, vì chúng ta còn một mớ việc phải làm tiếp theo.

Như thiết lập đường link cho bài viết, tạo các chuyên mục, tạo Menu, Logo, và các Plugin hỗ trợ SEO….

#4. Chỉnh sửa thông tin tài khoản User trên WordPress

WordPress cho phép quản trị viên có quyền thêm tài khoản truy cập vào trang admin. Vì vậy, ở tab Users các bạn có thể tùy chỉnh các thông tin liên quan đến người được truy cập vào trang này.

Ở đây mình có một tài khoản là blogchiasekienthuc – tài khoản này có quyền admin. Các bạn có thể bấm vào để chỉnh sửa thông tin.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (19)

Đầu tiên là một số cài đặt liên quan đến giao diện và trải nghiệm người dùng. Các bạn có thể lựa chọn sao cho phù hợp là được.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (20)

Tiếp theo là phần cài đặt liên quan đến tên đăng nhập, phần này các bạn có thể hoặc không cần bổ sung cũng được.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (21)

Cuối cùng là phần các bạn có thể thay đổi ảnh đại diện (hiển thị kèm theo bài đăng) nếu bạn là tác giả của bài đăng đó hoặc đổi mật khẩu khi cần thiết.

nhung-buoc-can-lam-ngay-sau-khi-cai-dat-wordpress (22)

#5. Lời kết

Vâng, như vậy là trong bài viết này mình đã hướng dẫn xong cho các bạn thực hiện một số thao tác cơ bản ngay sau khi các bạn cài đặt thành công WordPress rồi nhé.

Còn rất nhiều những thao tác quan trọng khác để chúng ta có thể hoàn thiện một trang web và mình sẽ cùng các bạn tìm hiểu trong các bài viết sau. Hẹn gặp lại các bạn trong những bài viết tiếp theo nhé !

CTV: Nguyễn Đức Cảnh – Bài viết gốc được đăng tải tại blogchiasekienthuc.com

Có thể bạn quan tâm:

Xem thêm việc làm công nghệ hấp dẫn trên TopDev

Nói về ServletContext và ServletConfig trong Jakarta EE Servlet

jakarta ee servlet
Nói về ServletContext và ServletConfig trong Jakarta EE Servlet

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

ServletContext là một đối tượng được khởi tạo bởi Web Container khi chúng ta deploy ứng dụng Jakarta EE web application. Nó được sử dụng lưu trữ và lấy thông tin mà chúng ta cấu hình cho tất cả các Servlet có trong ứng dụng. Còn ServletConfig thì giúp chúng ta lấy thông tin được định nghĩa cho một Servlet cụ thể nào đó. Cụ thể như thế nào? Trong bài viết này, mình sẽ thảo luận với các bạn về ServletContext và ServletConfig trong Jakarta EE Servlet các bạn nhé!

  Tạo ứng dụng Jakarta EE MVC sử dụng Maven trong Eclipse
  Định nghĩa request URL trong ứng dụng Jakarta EE RESTful Web Services

Xem thêm các việc làm COBOL hấp dẫn trên TopDev

Đầu tiên, mình sẽ mới một Jakarta EE Servlet Maven project để làm ví dụ:

Bây giờ, mình sẽ tạo mới 2 Servlet:

HelloWorldServlet

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Hello Servlet");
}
}

và HuongDanJavaServlet:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HuongDanJavaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Huong Dan Java Servlet");
}
}

rồi khai báo chúng trong tập tin web.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">

<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HelloWorldServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>huongdanjavaServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HuongDanJavaServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>huongdanjavaServlet</servlet-name>
<url-pattern>/huongdanjava</url-pattern>
</servlet-mapping>
</web-app>

Kết quả khi chạy ứng dụng, request tới http://localhost:8080/hello như sau:

Và request tới http://localhost:8080/huongdanjava như sau:

Nếu các bạn định nghĩa thông tin cho tất cả các Servlet trong ứng dụng sử dụng một hoặc nhiều tag <context-param> trong tập tin web.xml như sau:

<context-param>
<param-name>name</param-name>
<param-value>Khanh</param-value>
</context-param>

<context-param>
<param-name>website</param-name>
<param-value>Huong Dan Java</param-value>
</context-param>

Khi đó, trong cả 2 Servlet mà mình định nghĩa ở trên đều có thể lấy được thông tin cấu hình này bằng cách sử dụng đối tượng ServletContext, ví dụ như sau:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Hello Servlet");

ServletContext servletContext = getServletContext();
out.println(servletContext.getInitParameter("name"));
out.println(servletContext.getInitParameter("website"));
}
}

Hay:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HuongDanJavaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Huong Dan Java Servlet");

ServletContext servletContext = getServletContext();
out.println(servletContext.getInitParameter("name"));
out.println(servletContext.getInitParameter("website"));
}
}

Phương thức getServletContext() sẽ trả về đối tượng ServletContext được manage bởi Web Container mà ứng dụng đang được deploy, được khởi tạo lúc chúng ta deploy ứng dụng.

Kết quả:

Hay:

Còn nếu các bạn định nghĩa thông tin cho một Servlet cụ thể nào đó bằng cách khai báo tag <init-param> bên trong tag <servlet>, thì thông tin này sẽ không available với các Servlet khác của ứng dụng và chúng ta lấy những thông tin này sử dụng ServletConfig.

Ví dụ bây giờ, mình khai báo tập tin web.xml như sau:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">

<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HelloWorldServlet</servlet-class>
<load-on-startup>1</load-on-startup>

<init-param>
<param-name>name</param-name>
<param-value>Khanh</param-value>
</init-param>

<init-param>
<param-name>website</param-name>
<param-value>Huong Dan Java</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>huongdanjavaServlet</servlet-name>
<servlet-class>com.huongdanjava.jakartaee.servlet.HuongDanJavaServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>huongdanjavaServlet</servlet-name>
<url-pattern>/huongdanjava</url-pattern>
</servlet-mapping>
</web-app>

thì với đoạn code ở trên, chúng ta sẽ không lấy được thông tin context-param nữa:

Và:

Các bạn có thể lấy thông tin được khai báo trong HelloServlet bằng cách sử dụng ServletConfig như sau:

package com.huongdanjava.jakartaee.servlet;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Writing the message on the web page
PrintWriter out = resp.getWriter();
out.println("Hello Servlet");

ServletConfig servletConfig = getServletConfig();
out.println(servletConfig.getInitParameter("name"));
out.println(servletConfig.getInitParameter("website"));
}
}

Kết quả:
Request tới http://localhost:8080/huongdanjava, các bạn sẽ thấy kết quả như sau:

Còn nhiều thông tin khác mà các bạn có thể cấu hình với Jakarta EE Servlet và cũng có thể sử dụng ServletContext và ServletConfig để lấy những thông tin này các bạn nhé!