Taogen's Blog

Stay hungry stay foolish.

Local Environment

Check if your network status is OK and if you can access other websites.

Try a different browser or clear browser cache.

Check if the HOSTS file maps the domain name to 127.0.0.1 or an incorrect IP address. You can see what IP address the domain name is mapped to with the following command line.

ping {your_domain}

Check if you are using a HTTP proxy or if the proxy can access the website.

DNS Settings

Check that the A record of the DNS configuration points the domain name to the correct IP address.

Cloud Host

Check if the cloud host is on and accessible. You can use the following command line to check if cloud host is on.

ping {IP_address}

Check if the HTTP (80) or HTTPS (443) port is allowed in the firewall.

telnet {IP_address} 80
telnet {IP_address} 443

Check that the reverse proxy server listening on port 80 or 443 is running. You can find out what process is using 80 or 443 with the following command line.

sudo lsof -i -P -n | grep LISTEN

Check if the configuration of the reverse proxy server is correct.

Check if the actual web project is up and running. You can view all processes with the following command line.

ps -ef

Check that the cloud host operating system has sufficient resources such as CPU, memory, disk, and bandwidth. You can view the resource usage of the cloud host in the cloud platform console or view usage by command lines.

功能介绍

阿里云视频点播(VoD)提供了很多视频相关的功能。主要流程是上传视频,处理视频和播放视频。具体介绍可以参考视频点播产品简介。下图为视频点播的架构图。

视频点播架构图

准备工作

本节目标:开通服务,以及获取可以调用视频点播 SDK 和 API 的 accessKeyId 和 accessKeySecret。

开通视频点播服务

访问视频点播控制台页面,点击开通服务即可。第一次进入会提示开通服务,之后会直接进入点播控制台。

启用存储视频的Bucket

视频点播服务的视频存储可以使用点播系统Bucket,也可以使用OSS自有Bucket。

使用点播系统Bucket需要手动启用,操作如下:

视频点播控制台-> 配置管理 -> 存储管理 -> 顶部栏,选择地区:华东2(上海)-> 待分配 点播系统bucket:启用

点播系统Bucket上的视频使用视频点播相关功能会更方便。但如果你之前使用了阿里云的OSS,想要对你OSS上的视频使用视频点播相关功能也是可以的,就是会麻烦一点,需要多调用一个注册媒资信息接口。

这两种 bucket 有很多不同之处。详情可以查看视频点播-开发指南-存储管理视频点播-配置管理-存储管理

创建用户和授权

阿里云控制台 -> 右上角账号名称 -> 访问控制 -> 用户 -> 点击创建用户 -> 输入登录名称和显示名称,勾选 OpenAPI 调用访问 -> 点击确定 -> 保存用户的 accessKeyId 和 accessKeySecret

创建用户成功后,进入用户详情页面,为用户授予权限。

权限管理 -> 新增授权 -> 添加 “AliyunVODFullAccess 管理视频点播服务(VOD)的权限”

开发环境

本节目标:引入视频点播相关的 Java SDK。

Maven依赖

  • aliyun-java-sdk-core为核心库
  • aliyun-java-sdk-vod为VOD库
  • aliyun-java-sdk-kms为密钥管理服务(KMS)的所需依赖,若不涉及则无需添加。
  • com.aliyun.vod:upload 视频点播上传SDK没有开源,需要手动下载jar包引入到项目中。详情参考 SDK简介与下载-上传SDK
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
<version>2.16.10</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-kms</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>com.aliyun.vod</groupId>
<artifactId>upload</artifactId>
<version>1.4.15</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/aliyun-java-vod-upload-1.4.15.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230227</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>

调用接口

本节目标:了解接口调用流程和基本使用。

SDK的使用流程如下:

上传视频的代码示例

private static String uploadVideoToVOD(String fileName, InputStream inputStream) {
UploadStreamRequest request = new UploadStreamRequest(
accessKeyId, accessKeySecret, fileName, fileName, inputStream);
request.setAccessKeyId(accessKeyId);
request.setAccessKeySecret(accessKeySecret);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadStreamResponse response = uploader.uploadStream(request);
System.out.print("RequestId=" + response.getRequestId() + "\n"); //请求视频点播服务的请求ID
String videoId = response.getVideoId();
if (response.isSuccess()) {
System.out.print("VideoId=" + response.getVideoId() + "\n");
} else { //如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
System.out.print("VideoId=" + response.getVideoId() + "\n");
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}
return videoId;
}

/**
* 获取上传结果
*
* 用于检查是否上传成功,视频上传成功后才能进行视频处理
*
* @param videoId
* @return
* @throws ClientException
*/
private static GetUploadDetailsResponse.UploadDetail getUploadDetails(String videoId) throws ClientException {
GetUploadDetailsRequest request = new GetUploadDetailsRequest();
request.setMediaIds(videoId);
GetUploadDetailsResponse acsResponse = initVodClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET).getAcsResponse(request);
return acsResponse.getUploadDetails().get(0);
}

创建 DefaultAcsClient,请求视频AI处理的代码示例

private static String submitAiJob(String videoId) throws ClientException {
SubmitAIJobRequest request = new SubmitAIJobRequest();
request.setMediaId(videoId);
request.setTypes("AIVideoTag");
// Face,ASR,OCR,Category,Annotation
request.setConfig("{\"AIVideoTag\": {\"AnalyseTypes\": \"ASR,OCR,Annotation\"} }");
SubmitAIJobResponse acsResponse = initVodClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET).getAcsResponse(request);
String jobId = acsResponse.getAIJobList().get(0).getJobId();
System.out.println("jobId: " + jobId);
return jobId;
}

private static ListAIJobResponse.AIJob listAiJob(String jobId) throws ClientException {
ListAIJobRequest listAIJobRequest = new ListAIJobRequest();
listAIJobRequest.setJobIds(jobId);
ListAIJobResponse acsResponse = initVodClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET).getAcsResponse(listAIJobRequest);
ListAIJobResponse.AIJob aiJob = acsResponse.getAIJobList().get(0);
return aiJob;
}

public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) {
String regionId = "cn-shanghai"; // 点播服务接入地域
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}

视频点播的服务端 Java SDK 的使用

  • 创建一个对应视频处理的 Request 对象。SDK 的 request 对象和 API 上写的的基本一致,API 名称加个 Request 后缀即可。API 上有详情的参数说明,可结合使用。
  • 创建一个 VodClient 对象。
  • 获取对应的视频处理 Response 对象。

其他

查看费用

阿里云控制台 -> 顶部栏 “费用” -> 账单详情 -> 明细账单。

账单明细会有延迟,不能看到实时的消费明细。

明细账单数据相对于实际费用消耗延迟24小时更新,其中实例ID相关的信息( 实例配置,实例规格,实例昵称,资源组,公网IP,私网IP,可用区)延迟48小时更新。

关闭服务

视频点播服务没有统一的关闭入口。但可以在视频点播控制台中删除所有付费项。比如:删除掉所有域名,清空所有存储,取消转码和调用。

HTML & CSS

HTML: HyperText Markup Language

CSS: Cascading Style Sheets

Tags and Attributes of HTML Elements

Block Elements

<div>: document divisions

<h1>-<h6>: headings

<p>: paragraphs

<ul>: unordered lists

<ol>: ordered lists

<li>: list item

<pre>: preformatted text

<hr>: horizontal rules

Inline Elements

<img>: image

  • src: source
  • alt: alternative

<a>: anchor

  • href: hypertext reference

<br>: line break

References

Center a Div

The HTML example

<div class="container" style="height: 200px; width: 100%;">
<div class="child" style="width: 500px; height: 100px;">
I am the child
</div>
</div>

Horizontally Center a Div

Using margin

.container {
}
.child {
margin: 0 auto;
}

Using flex

.container {
display: flex;
justify-content: center;
}
.child {
}

Using position

.container {
position: absolute;
}
.child {
position: absolute;
left: 50%;
transform: translate(-50%);
}

Vertically Center a Div

Using flex

.container {
display: flex;
align-items: center;
}
.child {
}

Using position

.container {
position: absolute;
}
.child {
position: absolute;
top: 50%;
transform: translate(0, -50%);
}

Horizontally and Vertically Center a Div

Using flex

.container {
display: flex;
align-items: center;
justify-content: center;
}
.child {
}

Using grid

.container {
display: grid;
place-items: center;
}
.child {
}

Using position

.container {
position: absolute;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

Using margin and grid

.container {
display: grid;
}
.child {
margin: auto;
}

Using margin and flex

.container {
display: flex;
}
.child {
margin: auto;
}

Center Text

The HTML example

<div class="container" style="width: 500px; height: 100px;">
I am the text
</div>

Horizontally Center Text

Using text-align

.container {
text-align: center;
}

Using flex

.container {
display:flex;
justify-content: center;
}

Vertically Center Text

Using height and line-height

center a single line text

.container {
height: 50px;
line-height: 50px;
}

Using table-cell and vertical-align

.container {
display:table-cell;
vertical-align:middle;
}

Using pading-top and padding-bottom

.container {
padding-top: 100px;
padding-bottom: 100px;
}

Using flex

.container {
display:flex;
align-items:center;
}

Horizontally and Vertically Center Text

Using text-align + line-height

Using text-align + table-cell and vertical-align

Using text-align + padding top and bottom

Using flex

.container {
display:flex;
align-items:center;
justify-content: center;
}

Buy your blog website domain

You can buy a domain from the following domain registers:

Installing Hexo

Before installing Hexo, you need installing the following software

  • Node.js (Should be at least Node.js 10.13, recommends 12.0 or higher)
  • Git

Installing Hexo

npm install -g hexo-cli
hexo -v

Uninstalling Hexo

npm uninstall -g hexo-cli

Create your Hexo project

cd hexo-site
hexo init .
npm install
npm install hexo

Configuring Hexo

Configuring basic website information in _config.yml

Setting Description
title The title of your website
subtitle The subtitle of your website. (slogan)
description The description of your website
author Your name

Run Hexo

cd hexo-site
hexo s --debug

Visit http://localhost:4000

NexT Theme

Installation NexT

If you’re using Hexo 5.0 or later, the simplest way to install is through npm.

cd hexo-site
npm install hexo-theme-next

Upgrading NexT

cd hexo-site
npm install hexo-theme-next@latest

Enable NexT

Enable NexT in _config.yml

theme: next

Checking NexT

hexo clean
hexo s --debug

Visit http://localhost:4000

Settings

Copy default NexT configuration file to root path

cp node_modules/hexo-theme-next/_config.yml _config.next.yml

Enable cache in _config.next.yml

cache:
enable: true

Enable minify in _config.next.yml

minify: true

Set Gemini Scheme in _config.next.yml

#scheme: Muse
#scheme: Mist
#scheme: Pisces
scheme: Gemini

Enable icons and badges in _config.next.yml

menu_settings:
icons: true
badges: true

Disable motion to load web pages faster in _config.next.yml

motion:
enable: false

Update codeblock settings in _config.next.yml. Choose your favorite code block theme on NexT Highlight Theme Preview. Recommend highlight themes: stackoverflow-light, intellij-light.

codeblock:
theme:
light: intellij-light
dark: intellij-light
copy_button:
enable: true

Disable line number in _config.yml (Optional)

highlight:
line_number: false

prismjs:
line_number: false

Enable scroll percent in _config.next.yml

back2top:
scrollpercent: true

Update favicon in _config.next.yml

favicon:
small: /favicon_16x16.ico
medium: /favicon_32x32.ico
apple_touch_icon: /favicon.jpg
safari_pinned_tab: /favicon.jpg

Adding Page

Enable menu home, about, tags, categories, achives in _config.next.yml

menu:
home: / || fa fa-home
about: /about/ || fa fa-user
tags: /tags/ || fa fa-tags
categories: /categories/ || fa fa-th
archives: /archives/ || fa fa-archive

Home and Archives pages exist by default.

We need to add tags, categories, and about pages

hexo new page tags && hexo new page categories && hexo new page about

Setting page type

  • source/tags/index.md: type: tags
  • source/categories/index.md: type: categories
  • source/about/index.md: type: about

For example:

title: Tags
date: xxx
type: tags

Re-run your hexo project to see the new theme settings.

hexo s --debug

Add Search Service

Add the Local Search

Installation

npm install hexo-generator-searchdb

Configuring Hexo config file _config.yml. Add the following configurations to the _config.yml file

search:
path: search.xml
field: post
content: true
format: html

Configuring NexT config file _config.next.yml

local_search:
enable: true

Deploying your Hexo website

Push your Hexo project to GitHub

Create a new git repository on GitHub

Push your hexo project to GitHub

git init .
git add -A
git commit -m "Initialize Hexo project"
git remote add origin git@github.com:tagnja/{your-repository-name}.git
git branch -M main
git push -u origin main

Deploying Hexo website to Vercel

Add new project on Vercel

Import Git Repository

Deploy the project

Domain settings for your project on Vercel

Add your domain in Vercel -> hexo project -> Settings -> Domains

Add vercel dns to your domain register

ns1.vercel-dns.com
ns2.vercel-dns.com

Visit your domain in web browser

References

Create a spring boot project

You can go to Spring Initializr to create a spring boot project

Setting your basic configurations. The following is my settings

  • Project: Maven
  • Language: Java
  • Spring Boot: 2.7.9
  • Project Metadata:
    • Group: com.taogen
    • Aritifact: chatgpt-demo
    • Packaging: Jar
    • Java: 8

You can set your own configurations according to your local environment.

After setting the project configurations, we need to add the following dependencies:

  • Spring Web
  • Lombok

Then we click the GENERATE button to download our project file.

After downloading the generated project file, we need to decompress the file and open the directory in our IDE. I prefer to use IntelliJ IDEA as my IDE.

Call the ChatGPT API in spring boot

Configuring your Open AI key in application.properties

openai.apiKey=${YOUR_OPEN_AI_KEY}

Create the RestTemplateConfig.java to build the RestTemplate bean for API calls

@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}

Create a POJO ChatGpt3dot5Request.java for building ChatGPT API requests

@Data
public class ChatGpt3dot5Request {
public static final String ENDPOINT = "https://api.openai.com/v1/chat/completions";

public static final String STREAM_MESSAGE_PREFIX = "data: ";

private String model = "gpt-3.5-turbo";

private Boolean stream;
List<Message> messages = new ArrayList<>();

@Data
@AllArgsConstructor
static class Message {
private String role;
private String content;
}
}

Create the controller ChatBotController.java

@RequestMapping("/chatbot")
@RestController
public class ChatBotController {

@Autowired
public RestTemplate restTemplate;

@Value("${openai.apiKey}")
private String OPEN_API_KEY;

@PostMapping("/conversation")
public void conversation(@RequestBody String prompt, HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setContentType("text/plain; charset=UTF-8");
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(ChatGpt3dot5Request.ENDPOINT)
.queryParams(null);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setBearerAuth(OPEN_API_KEY);
String json = getRequestBody(prompt);
HttpEntity<Object> requestEntity =
new HttpEntity<>(json, httpHeaders);
ResponseEntity<Resource> responseEntity = restTemplate.exchange(builder.build().toUriString(), HttpMethod.POST, requestEntity, org.springframework.core.io.Resource.class);
PrintWriter writer = httpServletResponse.getWriter();
BufferedReader bufferedReader;
try {
bufferedReader = new BufferedReader(new InputStreamReader(responseEntity.getBody().getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null && !(ChatGpt3dot5Request.STREAM_MESSAGE_PREFIX + "[DONE]").equals(line)) {
String message = getMessageFromLine(line, ChatGpt3dot5Request.STREAM_MESSAGE_PREFIX);
writer.write(message);
writer.flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static String getMessageFromLine(String line, String prefix) throws JsonProcessingException {
if (!StringUtils.hasLength(line.trim())) {
return "";
}
String messageJsonStr = line.substring(line.indexOf(prefix) + prefix.length());
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = (ObjectNode) objectMapper.readTree(messageJsonStr);
JsonNode messageNode = objectNode.get("choices").get(0).get("delta").get("content");
if (messageNode != null) {
return messageNode.asText();
} else {
return "";
}
}

private String getRequestBody(String prompt) throws JsonProcessingException {
ChatGpt3dot5Request chatGpt3dot5Request = new ChatGpt3dot5Request();
chatGpt3dot5Request.setStream(true);
chatGpt3dot5Request.setMessages(Arrays.asList(
new ChatGpt3dot5Request.Message("user", prompt)));
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(chatGpt3dot5Request);
return json;
}
}

The ChatBotController sends an HTTP request to ChatGPT API and reads its response stream line by line. Each line of the response has a similar structure. We extract the chat content by removing the prefix data: , parsing the string to a JSON object, and retrieving the content value. This content is then written into the response of our chatbot API. The following is an example of the content of the ChatGPT API response.

Click to expand!
data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"role":"assistant"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"Hi"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" there"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"!"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" How"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" can"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" I"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" assist"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" you"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" today"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"?"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}

data: [DONE]

Call your spring boot application API

Send a “hello” prompt to ChatGPT API by your API

curl -d 'hello' -H 'Content-Type: application/json' -X POST http://localhost:8080/chatbot/conversation
Hi there! How can I assist you today?

Note: If ChatGPT is not available in your region, you will not be able to call the ChatGPT API and the request will time out. Your application may throw the following exception:

2023-03-16 14:40:22.680 ERROR 14704 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://api.openai.com/v1/engines/davinci-codex/completions": Connection timed out: connect; nested exception is java.net.ConnectException: Connection timed out: connect] with root cause
java.net.ConnectException: Connection timed out: connect

Java

1. Use logging. Never use system.out.print().

For long-running programs, you’ll need to look at the log files to check that the program is functioning correctly.

2. For the scheduled task thread or any thread other than the main thread, catch the entire code block exception.

Generally, the main thread has global exception handling, but other threads do not.

3. No empty catch blocks.

An empty catch block won’t give you any information about what exception occurred. Probably the exception doesn’t matter, but it’s still an exceptional case.

4. Separate business and functional code.

Businesses are changing and functions are shared and reusable.

5. Divide your code by domain rather than by functionality or class type.

Usually, changes in your application are related to a specific domain, you should focus on business domains when building your structures, not functionality or class type.

6. Don’t create unnecessary variables, objects in a loop block.

Be careful when creating objects in a loop block. Create objects before a loop block if possible. Don’t create a lot of unnecessary objects in a loop block. For example, java.text.SimpleDateFormat, org.apache.poi.xssf.usermodel.XSSFCellStyle objects.

7. Don’t use Java internal code. All classes in com.sun.*, sun.*, or jdk.*are notionally internal, and should not be used directly in your code. All the classes you can use are in the Java API documentation, such as java.*, javax.*, and org.*.

Data Access

1. Never execute SQL statements in a for loop.

It takes a lot of time. You should find a way to do it in bulk.

Java Web and Spring Boot

1. Scheduling tasks should run when a specific profile is activated.

If the project is deployed on multiple servers, each server will execute scheduled tasks. Even if it activates different profiles on different servers.

2. API URLs use domain names rather than IP addresses.

If you use IP-based API URLs in many projects, you need to change many projects whenever the IP address changes.

3. Use Environment Variables in spring boot configuration files.

Don’t expose your confidential information in source code.

1. Use a new git branch to develop new features. After finishing a new feature, switch to the main branch.

It’s much clearer when you’re doing multiple functions at the same time.

2. The committed code needs to push to the remote repository as soon as possible.

Code in a local environment can easily get lost. One time I deleted the entire project directory and my code was lost.

3. The test code is for testing only. Don’t use test code as your functional source code.

4. Use environment variables for configurations. Don’t hardcode configuration.

It’s safe and convenient.

5. Don’t keep uncommitted changes of code all the time. Either commit and push it to a remote branch or restore it.

First, uncommitted and unpushed changes are easily lost. Second, keep uncommitted changes makes new updates to the same file difficult. Third, It also messes up code commits.

If the changes are important and you won’t need them for a while, you can copy and save them to another place.

6. First, solve the problem. Then, write the code. Before writing code, you need to understand and organize the requirements and complete the database design, API design, and detailed design.

It makes development easier.

7. Anything that can be automated should be automated.

The problem “cannot resolve symbol” means IDEA hasn’t successfully indexed your project code and dependent libraries.

There are two common problems that cause the “cannot resolve symbol” problems:

  1. Maven dependency configuration problems.
  2. IDEA problems.

Maven Configuration Problems

Ensure Maven dependency resolve correctly

Your project’s Maven dependencies must be resolved correctly. Otherwise, IDEA can’t index project files successfully.

To check that Maven dependencies are resolved correctly, you can execute the following command:

mvn -Dmaven.test.skip=true clean package

If you can’t pass the above command, then there is something wrong with your Maven configuration. You can update your pom.xml and run the above command again.

After you passed the above command by updating your pom.xml, you need to reload the maven project to download missing dependencies. Right click the pom.xml in IDEA -> Maven -> Reload project. If the “cannot resolve symbol” errors are gone, the problem is solved.

If you have passed the above command and reloaded the project but there are still the “cannot resolve symbol” problems in IDEA, then it’s IDEA problems not Maven. You can go to the “Problems with IDEA” section.

Common problems and solutions with Maven

Problem 1: The dependency cannot be found

This means that the dependency cannot be found from the remote Maven repository.

Error Information:

Could not resolve dependencies for project {your_project}: Failed to collect dependencies at {groupId}:{artifactId}.jar:{version}

Solutions:

Check if the groupId, artifactId or version of the dependency is correct.

Check if there is an unofficial repository defined in the project’s pom.xml or ~/.m2/settings.xml, try using Maven central repository.

<repository>
<id>central</id>
<name>Maven Central Repository</name>
<url>https://repo1.maven.org/maven2</url>
</repository>

Problem 2: dependency conflict

This means that multiple versions of a dependency are added or indirectly added to the Maven project. A dependency can only exist in one version. The actual version used causes the referenced method or class to be unable to be found.

Error Information:

NoClassDefFoundError, ClassNotFoundException, or NoSuchMethodError

Solutions

Use the “Maven Helper” plugin to check if there is a dependency conflict. You can try excluding unwanted versions by right-clicking on the version in the Maven Helper plugin window.

If your Maven project has a parent project, you also need to check whether the version defined in the parent project is compatible with your current project.

More tips

If you pom.xml is correct, but you still can’t pass mvn clean package. You can try to force update dependencies:

# force update the dependencies
mvn clean package -U

Problems with IDEA

Try to re-index your project files

Close the project or exit IDEA, then delete the .idea folder, then reopen project or restart IDEA. When there is no .idea folder in your project root path, IDEA will re-index project files automatically.

In addition to the .idea folder, if your project is a git repository, it’s recommended to delete all file ignored by git.

# delete git ignored files
git clean -dfx

After you re-indexed the project (delete .idea), if the “cannot resolve symbol” errors are gone, the problem is solved.

If the “cannot resolve symbol” errors still exist, you can try rebuild project. In top menu bar, Build -> Rebuild project.

If you have re-indexed the project (delete .idea) and rebuilt the project but the “cannot resolve symbol” errors still exist, you can go to the next step “Invalidate IDEA cache”.

Invalidate IDEA cache

IDEA cache also affects IDEA index project files. If your Maven configurations are right, but there are still “cannot resolve symbol” problems. You can try to Invalidate Caches:

File -> Invalidate Caches -> checked “clear file system cache and Local History”, and then click “Invalidate and Restart”.

Re-enter IDEA and waiting for “Update indexes” to complete. After “Update indexes” is done. Then we need rebulid the project:

Build -> click “Rebuild Project”.

After invalidated IDEA cache, if the “cannot resolve symbol” errors are gone, the problem is solved. Otherwise, You can try to re-index your project files again.

In this post, I will introduce several ways to find HTML DOM elements in JavaScript.

querySelector methods

Document or Element methods

querySelectorAll(selectors)

  • Parameters: the selectors parameter is a valid CSS selector string.
  • Return Value: returns a static (not live) NodeList representing a list of the document’s elements that match the specified group of selectors, or an empty NodeList in case of no matches.

querySelector(selectors)

  • Parameters: the selectors parameter is a valid CSS selector string.
  • Return Value: return an Element object representing the first element in the document that matches the specified set of CSS selectors, or null is returned if there are no matches.

Find by id

document.querySelector("#test")

Find by HTML tag

document.querySelectorAll("p")

Find by class names

document.querySelectorAll("div.className1")
// or
document.querySelectorAll(".className1")

Find by attributes

// match elements contain a attribute
document.querySelectorAll("elementName[attrName]")
// match attribute value
container.querySelectorAll("elementName[attrName='attrValue']");
// attribute value start with
container.querySelectorAll("elementName[attrName^='attrValue']");
// attribute value end with
container.querySelectorAll("elementName[attrName$='attrValue']");
// attribute value contains
container.querySelectorAll("elementName[attrName*='attrValue']");

and

document.querySelectorAll("div.className1.className2");
// or
document.querySelectorAll(".className1.className2");
document.querySelectorAll("div[attr1='value1'][attr2='value2']");
// or
document.querySelectorAll("[attr1='value1'][attr2='value2']");

or

document.querySelectorAll("div.className1, div.className2");
// or
document.querySelectorAll(".className1, .className2");

Find descendants, children, siblings with tag names

// descendants
document.querySelectorAll("div span");
// the first span descendant
document.querySelectorAll("div span:nth-of-type(1)");
// children
document.querySelectorAll("div>span");
// the first child element
document.querySelectorAll("div :nth-child(1)")
// siblings
document.querySelectorAll("div~img");
// adjacent sibling
document.querySelectorAll("div+img");
  • nth-child(n) : Selects the nth child of a parent, regardless of the type of element.
  • nth-of-type(n) : Selects the nth child of a specific type within its parent.

For more information about CSS selectors, you can see the CSS selectors documentation.

Traversing match elements

const matchedElements = document.querySelectorAll("div");
matchedElements.forEach((item) => {
console.log(item.innerText)
});

Filter elements

const matchedElements = document.querySelectorAll("div");
// querySelectorAll returns a NodeList not an Array. You can convert it to an Array
Array.from(matchedElements).filter((item) => item.classList.contains('note'));
const matchedElements = document.querySelectorAll("div");
Array.prototype.filter.call(
matchedElements,
(item) => item.classList.contains('note'),
);

getElementsBy methods

Document or Element methods

getElementById(id)

  • Return value: An Element object describing the DOM element object matching the specified ID, or null if no matching element was found in the document.

getElementsByClassName(className)

  • Parameters: className a string representing the class name(s) to match; multiple class names are separated by whitespace.
  • Return value: A live HTMLCollection of found elements.

getElementsByName(nameAttrValue)

  • Parameters: nameAttrValue the value of the name attribute of the element(s) we are looking for.
  • Return value: A live NodeList collection, meaning it automatically updates as new elements with the same name are added to, or removed from, the document.

getElementsByTagName(elementName)

  • Parameters: elementName a string representing the name of the elements. The special string * represents all elements.
  • Return value: A live HTMLCollection of found elements.

Traverse elements

const testElements = document.getElementsByClassName('test');
for (let i = 0; i < testElements.length; i++) {
console.log(testElements[i].innerText)
}

Filter elements

const matchedElements = document.getElementsByTagName("div");
Array.from(matchedElements).filter((item) => item.classList.contains('yourClassName'));
const matchedElements = document.getElementsByTagName("div");
Array.prototype.filter.call(
matchedElements,
(item) => item.classList.contains('yourClassName'),
);

The closest method

closest(selector) looks for the nearest ancestor that matches the CSS-selector.

Summary

Method Searches by Call on an element Live
querySelector CSS-selector Yes -
querySelectorAll CSS-selector Yes -
getElementById id - -
getElementsByName name - Yes
getElementsByTagName tag or '*' Yes Yes
getElementsByClassName class Yes Yes

References

[1] Searching: getElement*, querySelector*

[2] Document.querySelector() - mdn

[3] Document.querySelectorAll() - mdn

[4] Document.getElementsByClassName() - mdn

[5] CSS selectors - mdn

0%