Category

Chưa được phân loại

Cấu hình NginX cho websocket

Nếu bạn có một webserver có public websocket ra dưới một reverse proxy NginX, nếu không cấu hình đúng bạn sẽ có thể gặp các lỗi như 403 Forbidden, không thể subscribe được một topic qua server name public ra ngoài.

Sử dụng cấu hình tham khảo dưới đây cho NginX khi bạn có một websocket chạy qua nó.

    server {
        listen       8000;
        location / {
            proxy_pass http://127.0.0.1:8000;
        }
        # The second stage for websocket
        location /ws {
            proxy_pass http://127.0.0.1:8000;
            # this magic is needed for WebSocket
            proxy_http_version  1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

Hướng dẫn tạo database MariaDB/MySQL

Bài hướng dẫn này bao gồm các thông tin:
– Tạo một database tên: kuong
– Tạo một database user có tên kuonguser với mật khẩu là kuonguser
– Gán quyền cho user kuonguser với database kuong

  • Mở terminal để đăng nhập vào quản trị database
mysql -u root -p $rootpassword
  • Tạo database tên kuong
mysql> create database kuong default character set utf8 default collate utf8_bin;
  • Tạo tài khoản truy cập vào database tên kuonguser có mật khẩu là kuonguser, gán quyền truy cập cho tài khoản đó
mysql> GRANT ALL PRIVILEGES ON kuong.* to kuonguser@'%' IDENTIFIED BY 'kuonguser';
mysql> GRANT ALL PRIVILEGES ON kuong.* to kuonguser@'localhost' IDENTIFIED BY 'kuonguser';

Quên mật khẩu root của MariaDB trên Mac OS X

Để reset mật khẩu user root của MariaDB trên Mac OS X, các bạn thực hiện theo các bước sau:

Đảm bảo MariaDB đã được stop

# brew services stop mariadb

Sau đó thực hiện câu lệnh sau:

# mysqld --skip-grant-tables --user=mysql &

Tiếp theo kết nối vào mariadb server:

# mysql

Do việc quản lý account đã được disabled khi thực hiện câu lệnh ở trên với tham số –skip-grant-tables nên chúng ta phải thực thi câu lệnh dưới để để tải lại quyền, để có thể thay đổi mật khẩu:

# FLUSH PRIVILEGES;

Thực thi query sau đó thực hiện cập nhật mật khẩu của user root:

# ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_passowrd';

Kết quả như sau:

MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY 'Itsol@maria';
Query OK, 0 rows affected (0.006 sec)

Khởi động lại Mariadb:

# brew services restart mariadb

Bây giờ bạn đã có thể đăng nhập lại với user root và mật khẩu mới

# mysql -u root -p
Cuongs-MacBook-Pro-4:~ macbookpro$ mysql -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 9
Server version: 10.3.11-MariaDB Homebrew

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Hướng dẫn chuyển vùng và cài Google Assistant Tiếng Việt trên iOS

Đầu tháng 05/2019, Google Assistent đã chính thức hỗ trợ Tiếng Việt. Tuy nhiên hiện tại để sử dụng được Tiếng Việt, người dùng phải tải bản cập nhật mới nhất từ App Store Mỹ. Với những tài khoản đang để App Store tại Việt Nam cần chuyển đổi Quốc gia/Vùng sang Mỹ để thực hiện tải về.

Để chuyển vùng, bạn làm theo các bước sau:

  1. Mở App Store, bấm vào avatar tài khoản của bạn

2. Làm theo các bước sau:

Làm theo hướng dẫn như trên

Chọn Hoa Kỳ (United State)

Bấm Đồng ý (Agree)

Ở màn hình này phần thông tin thanh toán chọn None nhé ^^

Điền địa chỉ ở Mỹ, ví dụ như trên, bạn có thể điền cùng thông tin này.

Sau khi hoàn thành các bước này, bạn mở lại App Store, tìm kiếm với từ khóa “Google Assistant” sẽ có kết quả như sau:

Sau khi cài đặt thì bạn đã có thể sử dụng được Tiếng Việt với cô trợ lý vui tính của Google.

Những điểm mới của Java 12

Java 12 chính thức được tung ra vào ngày 19/03/2019 với rất nhiều tính năng mới. So với phiên bản gần nhất là 11 thì phiên bản JDK 12 có khoảng 90 điểm và 109 so với bản 10. Các thay đổi này tạm nhóm trong 4 nhóm chính như sau: Ngôn ngữ Java, thư viện, JVM (máy ảo java) và các tính năng khác.

Thay đổi về ngôn ngữ

Điểm đáng chú ý nhất trên phiên bản JDK 12 này là biểu thức switch (switch expression – JEP 325). Bằng cách mở rộng câu lệnh switch này mà lập trình viên có thể sử dụng như một câu lệnh hoặc một biểu thức.

Ví dụ về câu lệnh switch truyền thống:

int numLetters;
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break;
case TUESDAY:
numLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numLetters = 8;
break;
case WEDNESDAY:
numLetters = 9;
break;
default:
throw new IllegalStateException("Huh? " + day);
}

Bây giờ bạn có thể viết thành dạng sau:

int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> throw new IllegalStateException("Huh? " + day);
};

Quan sát vào cú pháp mới này ta có thể nhận thấy có 2 thay đổi ở đây. Thứ nhất là thay vì phải chia từng trường hợp một thì ta có thể gom thành 1 nhóm, phân tách nhau bởi dấu phẩy (“,”). Điểm thứ 2 là toán tử Lambda (->) được sử dụng để đơn giản hóa cách chúng ta thể hiện giá trị được trả về từ biểu thức switch.

Thư viện (Libraries)

Có một thay đổi nhỏ rất hữu ích đó là Stream API có 1 Collector mới, được cung cấp bởi lớp Collectors. Collector mới này thu được bằng cách sử dụng phương thức teeing. Teeing collector nhận vào 3 đối số, 2 collectors, và một BiFunction. Để hiểu rõ hơn về điểm này các bạn có thể xem sơ đồ sau:

Tất cả các giá trị từ input stream sẽ được truyền vào mỗi collector. Kết quả của mỗi collector sẽ được truyền đi dưới dạng đối số cho BiFunction để xử lý và đưa ra kết quả cuối cùng.

Để hiểu rõ hơn về khái niệm này, chúng ta có thể xem ví dụ đơn giản sau, về cách tính trung bình.

/* Assume Collectors is statically imported */
double average = Stream.of(1, 4, 2, 7, 4, 6, 5)
.collect(teeing(
summingDouble(i -> i), 
counting(),
(sum, n) -> sum / n));

Collector đầu tiên sẽ tính tổng của các số, collector thứ 2 sẽ thực hiện đếm số lượng số. BiFunction sẽ thực hiện lấy tổng chia số lượng ra số trung bình.

java.io

InputStream skipNBytes(long n): hàm này sẽ bỏ qua và từ chối chính xác n bytes dữ liệu. Nếu n bằng 0 hoặc nhỏ hơn thì sẽ không có byte nào được bỏ qua.

java.lang

Trong package java.lang có thêm một package con là: java.lang.constant

JVM Constants API cung cấp các loại tham chiếu tượng trưng để mô tả cho từng dạng hằng số (class, loadable constant, MethodHandleMethodHandle constant, and MethodType constant). Các class sau sẽ có thêm hàm describeConstable() 

  • Class
  • Double
  • Enum
  • Float
  • Integer
  • Long
  • String
  • MethodHandle
  • MethodType
  • VarHandle

Các class sau sẽ chứa 1 hàm resolveConstantDesc():

  • Double
  • Enum.EnumDesc
  • Float
  • Integer
  • Long
  • String

java.lang.Character

Các class con đã được cập nhật để thêm các khối Unicode mới.

  • Chess Symbols
  • Mayan numerals
  • Sogdian
  • Old Sogdian

java.lang.String

indent(): thêm các ký tự khoảng trắng vào đầu chuỗi. Nếu giá trị truyền vào là một số âm thì số lượng các ký tự khoảng trắng tương ứng ở đầu chuỗi sẽ bị lược bỏ.
transform(): phương thức này cho phép chúng ta áp dụng một biểu thức lambda vào một chuỗi. Ví dụ:

String str = "1000";       
Integer integer = str.transform(Integer::parseInt);
System.out.println(integer);

java.lang.invoke

VarHandle: thêm hàm toString, trả về một mô tả ngắn gọn
java.net.SecureCacheResponse và java.net.ssl.HttpsConnection có phương thức mới: getSSLSession()

java.nio.files

Class Files có thêm phương thức mới: mismatch(), phương thức này sẽ tìm và trả về vị trí đầu tiên của byte không match trong nội dung của 2 file, trả về -1L nếu match.

Danh sách các website và blog về Java uy tín

Top 10 website về Java

1. Javaworld

2. Java Code Geeks

3. InfoQ | Java

4. DZone

5. The Server Side

6. Jaxenter | Java

7. The Spring Blog

8. DeveloperWorks: Java technology

9. Baeldung

10. Voxxed

Top Java Developers Blogs (21 blogs)

1. Mkyong

2. Vogella | Java

3. Java Specialists Newsletter

4. Javarevisited

5. Java Papers | Java

6. Stephen Colebourne’s blog

7. ProgramCreek

8. Bozho’s tech blog

9. Miles to go 3.0 …

10. jOOQ Blog | Java

11. Petri Kainulainen

12. NoBlogDefFound

13. Vlad Mihalcea’s Blog

14. CodeFX

15. Thought on Java

16. A Java Geek

17. Jenkov Tutorials

18. Adam Bien’s Weblog

19. Java2S | Java

20. How to do in Java

21. Inspired by Actual Events

How to replace text in Word file using Apache POI

Last couple of days, I faced an issue with replace text in Microsoft Word file, using Apache POI library, version 3.15.

The existing solution to replace the text I did follow as below:

package org.kodejava.example.poi;

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.hwpf.usermodel.Section;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;

public class WordReplaceText {
    public static final String SOURCE_FILE = "lipsum.doc";
    public static final String OUTPUT_FILE = "new-lipsum.doc";

    public static void main(String[] args) throws Exception {
        WordReplaceText instance = new WordReplaceText();
        HWPFDocument doc = instance.openDocument(SOURCE_FILE);
        if (doc != null) {
            doc = instance.replaceText(doc, "o", "0");
            instance.saveDocument(doc, OUTPUT_FILE);
        }
    }

    private HWPFDocument replaceText(HWPFDocument doc, String findText, String replaceText) {
        Range r = doc.getRange();
        for (int i = 0; i < r.numSections(); ++i) {
            Section s = r.getSection(i);
            for (int j = 0; j < s.numParagraphs(); j++) {
                Paragraph p = s.getParagraph(j);
                for (int k = 0; k < p.numCharacterRuns(); k++) {
                    CharacterRun run = p.getCharacterRun(k);
                    String text = run.text();
                    if (text.contains(findText)) {
                        run.replaceText(findText, replaceText);
                    }
                }
            }
        }
        return doc;
    }

    private HWPFDocument openDocument(String file) throws Exception {
        URL res = getClass().getClassLoader().getResource(file);
        HWPFDocument document = null;
        if (res != null) {
            document = new HWPFDocument(new POIFSFileSystem(
                    new File(res.getPath())));
        }
        return document;
    }

    private void saveDocument(HWPFDocument doc, String file) {
        try (FileOutputStream out = new FileOutputStream(file)) {
            doc.write(out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The problem is: when I have multiple “key” on the documents which is appear on paragraphs or in the table, the above function cannot replace all of them. Some keys are still there, cannot be replaced. I found an other solution on github that submitted by deividasstr. This solution solved my issues. You can explore at: https://github.com/deividasstr/docx-word-replacer. There are some main java files:

  • OnWordFoundCallback.java
  • TextReplacer.java
  • WordFinder.java
  • WordReplacer.java
package com.xandryex.utils;

import org.apache.poi.xwpf.usermodel.XWPFRun;

import java.util.List;

interface OnWordFoundCallback {

    void onWordFoundInRun(XWPFRun run);
    void onWordFoundInPreviousCurrentNextRun(List<XWPFRun> runs, int currentRun);
}
package com.xandryex.utils;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFRun;

import java.util.List;
import java.util.regex.Pattern;

public class TextReplacer extends WordFinder {

    private static int DEFAULT_TEXT_POS = 0;

    private String replacement;
    private String bookmark;

    public void replaceInText(XWPFDocument document, String bookmark, String replacement) {
        this.replacement = replacement;
        this.bookmark = bookmark;
        findWordsInText(document, bookmark);
    }

    public void replaceInTable(XWPFDocument document, String bookmark, String replacement) {
        this.replacement = replacement;
        this.bookmark = bookmark;
        findWordsInTable(document, bookmark);
    }

    @Override
    public void onWordFoundInRun(XWPFRun run) {
        replaceWordInRun(run);
    }

    @Override
    public void onWordFoundInPreviousCurrentNextRun(List<XWPFRun> runs, int currentRun) {
        replaceWordInPreviousCurrentNextRuns(runs, currentRun);
    }

    private void replaceWordInPreviousCurrentNextRuns(List<XWPFRun> runs, int currentRun) {
        boolean replacedInPreviousRun = replaceRunTextStart(runs.get(currentRun - 1));
        if (replacedInPreviousRun) {
            deleteTextFromRun(runs.get(currentRun));
        } else {
            replaceRunTextStart(runs.get(currentRun));
        }
        cleanRunTextStart(runs.get(currentRun + 1));
    }

    private void deleteTextFromRun(XWPFRun run) {
        run.setText("", DEFAULT_TEXT_POS);
    }

    //replaceAll() first parameter is used as regex pattern so normally special chars have to be escaped.
    //Pattern.quote() transforms given string into literal where special chars are ignored, thus can be used without escaping
    private void replaceWordInRun(XWPFRun run) {
        String replacedText = run.getText(DEFAULT_TEXT_POS).replaceAll(Pattern.quote(bookmark), replacement);
        run.setText(replacedText, DEFAULT_TEXT_POS);
    }

    private boolean replaceRunTextStart(XWPFRun run) {
        String text = run.getText(DEFAULT_TEXT_POS);
        String remainingBookmark = getRemainingBookmarkStart(text, bookmark);
        if (!remainingBookmark.isEmpty()) {
            text = text.replace(remainingBookmark, replacement);
            run.setText(text, DEFAULT_TEXT_POS);
            return true;
        }
        return false;
    }

    private void cleanRunTextStart(XWPFRun run) {
        String text = run.getText(DEFAULT_TEXT_POS);
        String remainingBookmark = getRemainingBookmarkEnd(text, bookmark);
        text = text.replace(remainingBookmark, "");
        run.setText(text, DEFAULT_TEXT_POS);
    }

    private String getRemainingBookmarkEnd(String text, String bookmark) {
        if (!text.startsWith(bookmark)) {
            return getRemainingBookmarkEnd(text, bookmark.substring(1, bookmark.length()));
        } else {
            return bookmark;
        }
    }

    private String getRemainingBookmarkStart(String text, String bookmark) {
        if (!text.endsWith(bookmark)) {
            return getRemainingBookmarkStart(text, bookmark.substring(0, bookmark.length() - 1));
        } else {
            return bookmark;
        }
    }
}
package com.xandryex.utils;

import org.apache.poi.xwpf.usermodel.*;

import java.util.*;

abstract class WordFinder implements OnWordFoundCallback {

    private static int DEFAULT_POS = 0;
    private static int DEFAULT_LAST_USED_RUN = -1;

    private String bookmark;

    /**
     * Checks if XWPFDocument tables contain a given bookmark. Checks runs of all paragraphs if searchable text is in one or
     * scattered in runs around it. It does not check separate paragraphs if text is scattered amongst them.
     *
     * @param doc  XWPFDocument
     * @param word to be searched
     */
    void findWordsInTable(XWPFDocument doc, String word) {
        this.bookmark = word;
        for (XWPFTable t : doc.getTables()) {
            checkTable(t);
        }
    }

    /**
     * Checks if XWPFDocument text contains a given bookmark. Checks runs of all paragraphs if searchable text is in one or
     * scattered in runs around it. It does not check separate paragraphs if text is scattered amongst them.
     *
     * @param doc  XWPFDocument
     * @param word to be searched
     */
    void findWordsInText(XWPFDocument doc, String word) {
        this.bookmark = word;
        for (XWPFParagraph p : doc.getParagraphs()) {
            if (paragraphNotNullAndHasRuns(p)) {
                checkInParagraph(p);
            }
        }
    }

    private void checkTable(XWPFTable t) {
        if (t.getRows() == null) return;
        for (XWPFTableRow r : t.getRows()) {
            checkRow(r);
        }
    }

    private void checkRow(XWPFTableRow r) {
        if (r.getTableCells() == null) return;
        for (XWPFTableCell cell : r.getTableCells()) {
            checkCell(cell);
        }
    }

    private void checkCell(XWPFTableCell cell) {
        if (cell.getParagraphs() == null) return;
        for (XWPFParagraph p : cell.getParagraphs()) {
            if (paragraphNotNullAndHasRuns(p)) {
                checkInParagraph(p);
            }
        }
    }

    private void checkInParagraph(XWPFParagraph p) {
        List<XWPFRun> runs = p.getRuns();
        int lastUsedRun = DEFAULT_LAST_USED_RUN;
        for (int runIndex = 0; runIndex < runs.size(); runIndex++) {
            XWPFRun run = p.getRuns().get(runIndex);
            if (isRunNotNullAndNotEmpty(run)) {
                String text = run.getText(DEFAULT_POS);
                //System.out.println(runIndex + " " + text);  //Uncomment for printing the runs
                if (text.contains(bookmark)) {
                    onWordFoundInRun(run);
                    lastUsedRun = runIndex;
                } else if (nextRunHasText(runs, runIndex)
                        && !nextRunsText(runs, runIndex).contains(bookmark)
                        && isWordInPreviousCurrentNextRuns(runs, lastUsedRun, runIndex)) {
                    onWordFoundInPreviousCurrentNextRun(runs, runIndex);
                }
            }
        }
    }

    private boolean isWordInPreviousCurrentNextRuns(List<XWPFRun> runs, int lastUsedRun, int runIndex) {
        return isNotFirstRun(runIndex)
                && previousRunHasText(runs, runIndex)
                && previousRunWasNotUsed(lastUsedRun, runIndex)
                && lastThisNextRunText(runs, runIndex).contains(bookmark);
    }

    private boolean previousRunWasNotUsed(int lastUsedRun, int runIndex) {
        return lastUsedRun != runIndex - 1;
    }

    private boolean paragraphNotNullAndHasRuns(XWPFParagraph p) {
        return p != null && !p.getRuns().isEmpty();
    }

    private String lastThisNextRunText(List<XWPFRun> runs, int runIndex) {
        String text = runs.get(runIndex).getText(DEFAULT_POS);
        return lastAndCurrentRunsText(runs, runIndex, text) + nextRunsText(runs, runIndex);
    }

    private boolean nextRunHasText(List<XWPFRun> runs, int runIndex) {
        return runs.size() > runIndex + 1
                && runs.get(runIndex + 1).getText(DEFAULT_POS) != null
                && !runs.get(runIndex + 1).getText(DEFAULT_POS).isEmpty();
    }

    private String nextRunsText(List<XWPFRun> runs, int i) {
        return runs.get(i + 1).getText(DEFAULT_POS);
    }

    private String lastAndCurrentRunsText(List<XWPFRun> runs, int runIndex, String text) {
        return runs.get(runIndex - 1).getText(DEFAULT_POS) + text;
    }

    private boolean previousRunHasText(List<XWPFRun> runs, int runIndex) {
        return runs.get(runIndex - 1).getText(DEFAULT_POS) != null
                && !runs.get(runIndex - 1).getText(DEFAULT_POS).isEmpty();
    }

    private boolean isNotFirstRun(int runIndex) {
        return runIndex > 0;
    }

    private boolean isRunNotNullAndNotEmpty(XWPFRun run) {
        return run != null && run.getText(DEFAULT_POS) != null;
    }
}
package com.xandryex;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import com.xandryex.utils.TextReplacer;

import java.io.*;

public class WordReplacer {

    private XWPFDocument document;
    private TextReplacer replacer;

    /**
     * Creates WordReplacer with file to modify.
     *
     * @param docxFile file of type docx.
     * @throws IOException thrown if file is not found or is not required type.
     */
    public WordReplacer(File docxFile) throws IOException {
        InputStream inputStream = new FileInputStream(docxFile);
        init(new XWPFDocument(inputStream));
    }

    /**
     * Creates WordReplacer with XWPFDocument to modify.
     * @param xwpfDoc to modify.
     */
    public WordReplacer(XWPFDocument xwpfDoc) {
        init(xwpfDoc);
    }

    private void init(XWPFDocument xwpfDoc) {
        if (xwpfDoc == null) throw new NullPointerException();
        document = xwpfDoc;
        replacer = new TextReplacer();
    }

    /**
     * Replaces all occurrences of a bookmark only in the text of the file with a replacement string.
     * @param bookmark word to replace.
     * @param replacement word of replacement.
     */
    public void replaceWordsInText(String bookmark, String replacement) {
        replacer.replaceInText(document, bookmark, replacement);
    }

    /**
     * Replaces all occurrences of a bookmark only in tables of the file with a replacement string.
     * @param bookmark word to replace.
     * @param replacement word of replacement.
     */
    public void replaceWordsInTables(String bookmark, String replacement) {
        replacer.replaceInTable(document, bookmark, replacement);
    }

    /**
     * Most of the time we want our template files untouched. Creates file from path, saves the modified document to it and returns it.
     * @param path filepath (dirs + filename).
     * @return modified file.
     * @throws Exception thrown if some issues while saving occur - mostly due to unavailable file or permissions.
     */
    public File saveAndGetModdedFile(String path) throws Exception {
        File file = new File(path);
        return saveToFile(file);
    }

    /**
     * Most of the time we want our template files untouched. Saves the modified document to the given file and returns it.
     * @param file to save to.
     * @return modified file.
     * @throws Exception thrown if some issues while saving occur - mostly due to unavailable file or permissions.
     */
    public File saveAndGetModdedFile(File file) throws Exception {
        return saveToFile(file);
    }

    public XWPFDocument getModdedXWPFDoc() {
        return document;
    }

    private File saveToFile(File file) throws Exception {
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file, false);
            document.write(out);
            document.close();
            return file;
        } catch (Exception e) {
            throw e;
        } finally {
            if (out != null) {
                out.flush();
                out.close();
            }
        }
    }
}

Using:

public static final String REPLACED_WORD = "pogo";
public static final String REPLACED_WORD_2 = "stick";

public static final String TEXT_WITHOUT_BOOKMARK = "Something here";

private static File docxFile;
private static TextReplacer replacer;
private static WordCounter wordCounter;
private XWPFDocument document;

......
docxFile = new File("./src/test/resources/docxfile.docx");
replacer = new TextReplacer();
wordCounter = new WordCounter();

......
replacer.replaceInText(document, WordCounterTest.TEST_DOC_TEST_CASE, REPLACED_WORD);
replacer.replaceInTable(document, WordCounterTest.TEST_DOC_TEST_CASE, REPLACED_WORD);

Many thanks to deividasstr.