You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by GitBox <gi...@apache.org> on 2021/09/22 09:05:30 UTC

[GitHub] [skywalking] sonatype-lift[bot] commented on a change in pull request #7766: IoTDB storage plugin

sonatype-lift[bot] commented on a change in pull request #7766:
URL: https://github.com/apache/skywalking/pull/7766#discussion_r713743156



##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/iotdb/IoTDBClient.java
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.storage.plugin.iotdb;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.session.pool.SessionDataSetWrapper;
+import org.apache.iotdb.session.pool.SessionPool;
+import org.apache.iotdb.tsfile.exception.NullFieldException;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.common.Field;
+import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.manual.log.LogRecord;
+import org.apache.skywalking.oap.server.core.browser.manual.errorlog.BrowserErrorLogRecord;
+import org.apache.skywalking.oap.server.core.management.ui.template.UITemplate;
+import org.apache.skywalking.oap.server.core.storage.StorageData;
+import org.apache.skywalking.oap.server.core.storage.StorageHashMapBuilder;
+import org.apache.skywalking.oap.server.library.client.Client;
+import org.apache.skywalking.oap.server.library.client.healthcheck.DelegatedHealthChecker;
+import org.apache.skywalking.oap.server.library.client.healthcheck.HealthCheckable;
+import org.apache.skywalking.oap.server.library.util.HealthChecker;
+import org.apache.skywalking.oap.server.storage.plugin.iotdb.base.IoTDBInsertRequest;
+
+@Slf4j
+public class IoTDBClient implements Client, HealthCheckable {
+    private final DelegatedHealthChecker healthChecker = new DelegatedHealthChecker();
+    private final IoTDBStorageConfig config;
+
+    private SessionPool sessionPool;
+    private final String storageGroup;
+
+    public static final String DOT = ".";
+    public static final String ALIGN_BY_DEVICE = " align by device";
+
+    public static final String TIME_BUCKET = "time_bucket";
+    public static final String TIME = "Time";
+    public static final String TIMESTAMP = "timestamp";
+
+    public static final String ID_IDX = "id";
+    public static final String ENTITY_ID_IDX = "entity_id";
+    public static final String NODE_TYPE_IDX = "node_type";
+    public static final String GROUP_IDX = "service_group";
+    public static final String SERVICE_ID_IDX = "service_id";
+    public static final String TRACE_ID_IDX = "trace_id";
+
+    public IoTDBClient(IoTDBStorageConfig config) throws IOException {
+        this.config = config;
+        storageGroup = config.getStorageGroup();
+    }
+
+    public final String getStorageGroup() {
+        return storageGroup;
+    }
+
+    @Override
+    public void connect() throws IoTDBConnectionException, StatementExecutionException {
+        try {
+            sessionPool = new SessionPool(config.getHost(), config.getRpcPort(), config.getUsername(),
+                    config.getPassword(), config.getSessionPoolSize(), config.isRpcCompression());
+            sessionPool.setStorageGroup(storageGroup);
+
+            healthChecker.health();
+        } catch (StatementExecutionException e) {
+            if (e.getStatusCode() != TSStatusCode.PATH_ALREADY_EXIST_ERROR.getStatusCode()) {
+                healthChecker.unHealth(e);
+                throw e;
+            }
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        sessionPool.close();
+        this.healthChecker.health();
+    }
+
+    @Override
+    public void registerChecker(HealthChecker healthChecker) {
+        this.healthChecker.register(healthChecker);
+    }
+
+    public SessionPool getSessionPool() {
+        return sessionPool;
+    }
+
+    public IoTDBStorageConfig getConfig() {
+        return config;
+    }
+
+    /**
+     * Write data to IoTDB
+     *
+     * @param request an IoTDBInsertRequest
+     * @throws IOException IoTDBConnectionException or StatementExecutionException
+     */
+    public void write(IoTDBInsertRequest request) throws IOException {
+        StringBuilder devicePath = new StringBuilder();
+        devicePath.append(storageGroup).append(IoTDBClient.DOT).append(request.getModelName());
+        try {
+            // make an index value as a layer name of the storage path
+            if (!request.getIndexes().isEmpty()) {
+                request.getIndexValues().forEach(value -> devicePath.append(IoTDBClient.DOT)
+                        .append(indexValue2LayerName(value)));
+            }
+            sessionPool.insertRecord(devicePath.toString(), request.getTime(),
+                    request.getTimeseriesList(), request.getTimeseriesTypes(), request.getTimeseriesValues());
+            healthChecker.health();
+        } catch (IoTDBConnectionException | StatementExecutionException e) {
+            healthChecker.unHealth(e);
+            throw new IOException(e);
+        }
+    }
+
+    /**
+     * Write a list of data into IoTDB
+     *
+     * @param requestList a list of IoTDBInsertRequest
+     * @throws IOException IoTDBConnectionException or StatementExecutionException
+     */
+    public void write(List<IoTDBInsertRequest> requestList) throws IOException {
+        List<String> devicePathList = new ArrayList<>();
+        List<Long> timeList = new ArrayList<>();
+        List<List<String>> timeseriesListList = new ArrayList<>();
+        List<List<TSDataType>> typesList = new ArrayList<>();
+        List<List<Object>> valuesList = new ArrayList<>();
+
+        requestList.forEach(request -> {
+            StringBuilder devicePath = new StringBuilder();
+            devicePath.append(storageGroup).append(IoTDBClient.DOT).append(request.getModelName());
+            // make an index value as a layer name of the storage path
+            if (!request.getIndexes().isEmpty()) {
+                request.getIndexValues().forEach(value -> devicePath.append(IoTDBClient.DOT)
+                        .append(indexValue2LayerName(value)));
+            }
+            devicePathList.add(devicePath.toString());
+            timeList.add(request.getTime());
+            timeseriesListList.add(request.getTimeseriesList());
+            typesList.add(request.getTimeseriesTypes());
+            valuesList.add(request.getTimeseriesValues());
+        });
+
+        try {
+            sessionPool.insertRecords(devicePathList, timeList, timeseriesListList, typesList, valuesList);
+            healthChecker.health();
+        } catch (IoTDBConnectionException | StatementExecutionException e) {
+            healthChecker.unHealth(e);
+            throw new IOException(e);
+        }
+    }
+
+    /**
+     * Normal filter query for a list of data. querySQL must contain "align by device"
+     *
+     * @param modelName      model name
+     * @param querySQL       the SQL for query which must contain "align by device"
+     * @param storageBuilder storage builder for transforming storage result map to entity
+     * @return a list of result data
+     * @throws IOException IoTDBConnectionException or StatementExecutionException
+     */
+    public List<? super StorageData> filterQuery(String modelName, String querySQL,
+                                                 StorageHashMapBuilder<? extends StorageData> storageBuilder)
+            throws IOException {
+        if (!querySQL.contains("align by device")) {
+            throw new IOException("querySQL must contain \"align by device\"");
+        }
+        List<? super StorageData> storageDataList = new ArrayList<>();
+        try {
+            StringBuilder devicePath = new StringBuilder();
+            devicePath.append(storageGroup).append(IoTDBClient.DOT).append(modelName);
+            if (!sessionPool.checkTimeseriesExists(devicePath.toString())) {
+                return storageDataList;
+            }
+            SessionDataSetWrapper wrapper = sessionPool.executeQueryStatement(querySQL);
+            if (log.isDebugEnabled()) {
+                log.debug("SQL: {}, columnNames: {}", querySQL, wrapper.getColumnNames());
+            }
+
+            List<String> columnNames = wrapper.getColumnNames();
+            IoTDBTableMetaInfo tableMetaInfo = IoTDBTableMetaInfo.get(modelName);
+            List<String> indexes = tableMetaInfo.getIndexes();
+            while (wrapper.hasNext()) {
+                Map<String, Object> map = new ConcurrentHashMap<>();
+                RowRecord rowRecord = wrapper.next();
+                List<Field> fields = rowRecord.getFields();
+                // transform timestamp to time_bucket
+                if (!UITemplate.INDEX_NAME.equals(modelName)) {
+                    map.put(IoTDBClient.TIME_BUCKET, TimeBucket.getTimeBucket(rowRecord.getTimestamp(),
+                            tableMetaInfo.getModel().getDownsampling()));
+                }
+                // field.get(0) -> Device, transform layerName to indexValue
+                String[] layerNames = fields.get(0).getStringValue().split("\\" + IoTDBClient.DOT + "\"");
+                for (int i = 0; i < indexes.size(); i++) {
+                    map.put(indexes.get(i), layerName2IndexValue(layerNames[i + 1]));
+                }
+                for (int i = 0; i < columnNames.size() - 2; i++) {
+                    String columnName = columnNames.get(i + 2);
+                    Field field = fields.get(i + 1);
+                    if (field.getDataType() == null) {
+                        continue;
+                    }
+                    if (field.getDataType().equals(TSDataType.TEXT)) {
+                        map.put(columnName, field.getStringValue());
+                    } else {
+                        map.put(columnName, field.getObjectValue(field.getDataType()));
+                    }
+                }
+                if (map.containsKey(IoTDBClient.NODE_TYPE_IDX)) {
+                    String nodeType = (String) map.get(IoTDBClient.NODE_TYPE_IDX);
+                    map.put(IoTDBClient.NODE_TYPE_IDX, Integer.valueOf(nodeType));
+                }
+                if (modelName.equals(BrowserErrorLogRecord.INDEX_NAME) || modelName.equals(LogRecord.INDEX_NAME)) {
+                    map.put(IoTDBClient.TIMESTAMP, map.get("\"" + IoTDBClient.TIMESTAMP + "\""));
+                }
+                for (String key : map.keySet()) {
+                    // remove double quotes
+                    if (key.contains(".")) {
+                        map.put(key.substring(1, key.length() - 1), map.get(key));

Review comment:
       *INEFFICIENT_KEYSET_ITERATOR:*  Accessing a value using a key that was retrieved from a `keySet` iterator. It is more efficient to use an iterator on the `entrySet` of the map, avoiding the extra `HashMap.get(key)` lookup.
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)

##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/iotdb/profile/IoTDBProfileThreadSnapshotQueryDAO.java
##########
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.storage.plugin.iotdb.profile;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.manual.segment.SegmentRecord;
+import org.apache.skywalking.oap.server.core.profile.ProfileThreadSnapshotRecord;
+import org.apache.skywalking.oap.server.core.query.type.BasicTrace;
+import org.apache.skywalking.oap.server.core.storage.StorageData;
+import org.apache.skywalking.oap.server.core.storage.StorageHashMapBuilder;
+import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnapshotQueryDAO;
+import org.apache.skywalking.oap.server.library.util.BooleanUtils;
+import org.apache.skywalking.oap.server.storage.plugin.iotdb.IoTDBClient;
+
+public class IoTDBProfileThreadSnapshotQueryDAO implements IProfileThreadSnapshotQueryDAO {
+    private final IoTDBClient client;
+    private final StorageHashMapBuilder<ProfileThreadSnapshotRecord> profileThreadSnapshotRecordBuilder = new ProfileThreadSnapshotRecord.Builder();
+    private final StorageHashMapBuilder<SegmentRecord> segmentRecordBuilder = new SegmentRecord.Builder();
+
+    public IoTDBProfileThreadSnapshotQueryDAO(IoTDBClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public List<BasicTrace> queryProfiledSegments(String taskId) throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT)
+                .append(ProfileThreadSnapshotRecord.INDEX_NAME);
+        query = client.addQueryAsterisk(ProfileThreadSnapshotRecord.INDEX_NAME, query);
+        query.append(" where ").append(ProfileThreadSnapshotRecord.TASK_ID).append(" = \"").append(taskId).append("\"")
+                .append(" and ").append(ProfileThreadSnapshotRecord.SEQUENCE).append(" = 0")
+                .append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(ProfileThreadSnapshotRecord.INDEX_NAME,
+                query.toString(), profileThreadSnapshotRecordBuilder);
+        // We can insure the size of List, so use ArrayList to improve visit speed. (Other storage plugin use LinkedList)
+        final List<String> segmentIds = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> segmentIds.add(((ProfileThreadSnapshotRecord) storageData).getSegmentId()));
+        if (segmentIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // This method maybe have poor efficiency. It queries all data which meets a condition without select function.
+        // https://github.com/apache/iotdb/discussions/3888
+        query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(SegmentRecord.INDEX_NAME);
+        query = client.addQueryAsterisk(SegmentRecord.INDEX_NAME, query);
+        query.append(" where ").append(SegmentRecord.SEGMENT_ID).append(" in (");
+        for (String segmentId : segmentIds) {
+            query.append("\"").append(segmentId).append("\"").append(", ");
+        }
+        String queryString = query.substring(0, query.length() - 2);
+        queryString = queryString + ")" + IoTDBClient.ALIGN_BY_DEVICE;
+
+        storageDataList = client.filterQuery(SegmentRecord.INDEX_NAME, queryString, segmentRecordBuilder);
+        List<SegmentRecord> segmentRecordList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> segmentRecordList.add((SegmentRecord) storageData));
+        // resort by self, because of the select query result order by time.
+        segmentRecordList.sort((SegmentRecord r1, SegmentRecord r2) -> Long.compare(r2.getStartTime(), r1.getStartTime()));
+
+        List<BasicTrace> result = new ArrayList<>(segmentRecordList.size());
+        segmentRecordList.forEach(segmentRecord -> {
+            BasicTrace basicTrace = new BasicTrace();
+            basicTrace.setSegmentId(segmentRecord.getSegmentId());
+            basicTrace.setStart(String.valueOf(segmentRecord.getStartTime()));
+            basicTrace.getEndpointNames().add(String.valueOf(IDManager.EndpointID.analysisId(segmentRecord.getEndpointId())));

Review comment:
       *NULL_DEREFERENCE:*  object returned by `getEndpointNames(basicTrace)` could be null and is dereferenced at line 86.
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)

##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/iotdb/query/IoTDBAlarmQueryDAO.java
##########
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.storage.plugin.iotdb.query;
+
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import org.apache.skywalking.oap.server.core.alarm.AlarmRecord;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.manual.searchtag.Tag;
+import org.apache.skywalking.oap.server.core.query.enumeration.Scope;
+import org.apache.skywalking.oap.server.core.query.type.AlarmMessage;
+import org.apache.skywalking.oap.server.core.query.type.Alarms;
+import org.apache.skywalking.oap.server.core.storage.StorageData;
+import org.apache.skywalking.oap.server.core.storage.StorageHashMapBuilder;
+import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
+import org.apache.skywalking.oap.server.library.util.CollectionUtils;
+import org.apache.skywalking.oap.server.storage.plugin.iotdb.IoTDBClient;
+
+public class IoTDBAlarmQueryDAO implements IAlarmQueryDAO {
+    private final IoTDBClient client;
+    private final StorageHashMapBuilder<AlarmRecord> storageBuilder = new AlarmRecord.Builder();
+
+    public IoTDBAlarmQueryDAO(IoTDBClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public Alarms getAlarm(Integer scopeId, String keyword, int limit, int from, long startTB, long endTB, List<Tag> tags) throws IOException {
+        StringBuilder query = new StringBuilder();
+        // This method maybe have poor efficiency. It queries all data which meets a condition without select function.
+        // https://github.com/apache/iotdb/discussions/3888
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(AlarmRecord.INDEX_NAME);
+        query = client.addQueryAsterisk(AlarmRecord.INDEX_NAME, query);
+        query.append(" where 1=1");
+        if (Objects.nonNull(scopeId)) {
+            query.append(" and ").append(AlarmRecord.SCOPE).append(" = ").append(scopeId);
+        }
+        if (startTB != 0 && endTB != 0) {
+            query.append(" and ").append(IoTDBClient.TIME).append(" >= ").append(TimeBucket.getTimestamp(startTB));
+            query.append(" and ").append(IoTDBClient.TIME).append(" <= ").append(TimeBucket.getTimestamp(endTB));
+        }
+        if (!Strings.isNullOrEmpty(keyword)) {
+            query.append(" and ").append(AlarmRecord.ALARM_MESSAGE).append(" like '%").append(keyword).append("%'");
+        }
+        if (CollectionUtils.isNotEmpty(tags)) {
+            for (final Tag tag : tags) {
+                query.append(" and ").append(tag.getKey()).append(" = \"").append(tag.getValue()).append("\"");
+            }
+        }
+        // IoTDB doesn't support the query contains "1=1" and "*" at the meantime.
+        String queryString = query.toString().replace("1=1 and ", "");
+        queryString = queryString + IoTDBClient.ALIGN_BY_DEVICE;
+
+        Alarms alarms = new Alarms();
+        List<? super StorageData> storageDataList = client.filterQuery(AlarmRecord.INDEX_NAME, queryString, storageBuilder);
+        int limitCount = 0;
+        for (int i = 0; i < storageDataList.size(); i++) {
+            if (i >= from && limitCount < limit) {
+                limitCount++;
+                AlarmRecord alarmRecord = (AlarmRecord) storageDataList.get(i);
+                alarms.getMsgs().add(parseMessage(alarmRecord));
+            }
+        }
+        alarms.setTotal(storageDataList.size());
+        // resort by self, because of the select query result order by time.
+        alarms.getMsgs().sort((AlarmMessage m1, AlarmMessage m2) -> Long.compare(m2.getStartTime(), m1.getStartTime()));

Review comment:
       *NULL_DEREFERENCE:*  object returned by `getMsgs(alarms)` could be null and is dereferenced at line 84.
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)

##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/iotdb/query/IoTDBEventQueryDAO.java
##########
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.storage.plugin.iotdb.query;
+
+import com.google.common.base.Strings;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import org.apache.skywalking.oap.server.core.query.PaginationUtils;
+import org.apache.skywalking.oap.server.core.query.enumeration.Order;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.EventType;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.query.type.event.Source;
+import org.apache.skywalking.oap.server.core.source.Event;
+import org.apache.skywalking.oap.server.core.storage.StorageData;
+import org.apache.skywalking.oap.server.core.storage.StorageHashMapBuilder;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.iotdb.IoTDBClient;
+
+public class IoTDBEventQueryDAO implements IEventQueryDAO {
+    private final IoTDBClient client;
+    private final StorageHashMapBuilder<Event> storageBuilder = new Event.Builder();
+
+    public IoTDBEventQueryDAO(IoTDBClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public Events queryEvents(EventQueryCondition condition) throws Exception {
+        // This method maybe have poor efficiency. It queries all data which meets a condition without select function.
+        // https://github.com/apache/iotdb/discussions/3888
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(Event.INDEX_NAME);
+        query = client.addQueryAsterisk(Event.INDEX_NAME, query);
+        query = whereSQL(condition, query);
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(Event.INDEX_NAME, query.toString(), storageBuilder);
+        final Events events = new Events();
+        int limitCount = 0;
+        PaginationUtils.Page page = PaginationUtils.INSTANCE.exchange(condition.getPaging());
+        for (int i = 0; i < storageDataList.size(); i++) {
+            if (i >= page.getFrom() && limitCount < page.getLimit()) {
+                limitCount++;
+                Event event = (Event) storageDataList.get(i);
+                org.apache.skywalking.oap.server.core.query.type.event.Event resultEvent = parseEvent(event);
+                events.getEvents().add(resultEvent);
+            }
+        }
+        events.setTotal(storageDataList.size());
+        // resort by self, because of the select query result order by time.
+        final Order order = Objects.isNull(condition.getOrder()) ? Order.DES : condition.getOrder();
+        if (Order.DES.equals(order)) {
+            events.getEvents().sort(
+                    (org.apache.skywalking.oap.server.core.query.type.event.Event e1,
+                     org.apache.skywalking.oap.server.core.query.type.event.Event e2)
+                            -> Long.compare(e2.getStartTime(), e1.getStartTime()));
+        } else {
+            events.getEvents().sort(
+                    Comparator.comparingLong(org.apache.skywalking.oap.server.core.query.type.event.Event::getStartTime));
+        }
+        return events;
+    }
+
+    @Override
+    public Events queryEvents(List<EventQueryCondition> conditionList) throws Exception {
+        // This method maybe have poor efficiency. It queries all data which meets a condition without select function.
+        // https://github.com/apache/iotdb/discussions/3888
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(Event.INDEX_NAME);
+        query = client.addQueryAsterisk(Event.INDEX_NAME, query);
+        query = whereSQL(conditionList, query);
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(Event.INDEX_NAME, query.toString(), storageBuilder);
+        final Events events = new Events();
+        EventQueryCondition condition = conditionList.get(0);
+        int limitCount = 0;
+        PaginationUtils.Page page = PaginationUtils.INSTANCE.exchange(condition.getPaging());
+        for (int i = 0; i < storageDataList.size(); i++) {
+            if (i >= page.getFrom() && limitCount < page.getLimit()) {
+                limitCount++;
+                Event event = (Event) storageDataList.get(i);
+                org.apache.skywalking.oap.server.core.query.type.event.Event resultEvent = parseEvent(event);
+                events.getEvents().add(resultEvent);
+            }
+        }
+        events.setTotal(storageDataList.size());
+        // resort by self, because of the select query result order by time.
+        final Order order = Objects.isNull(condition.getOrder()) ? Order.DES : condition.getOrder();
+        if (Order.DES.equals(order)) {
+            events.getEvents().sort(

Review comment:
       *NULL_DEREFERENCE:*  object returned by `getEvents(events)` could be null and is dereferenced at line 110.
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)

##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/iotdb/query/IoTDBEventQueryDAO.java
##########
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.storage.plugin.iotdb.query;
+
+import com.google.common.base.Strings;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import org.apache.skywalking.oap.server.core.query.PaginationUtils;
+import org.apache.skywalking.oap.server.core.query.enumeration.Order;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.EventType;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.query.type.event.Source;
+import org.apache.skywalking.oap.server.core.source.Event;
+import org.apache.skywalking.oap.server.core.storage.StorageData;
+import org.apache.skywalking.oap.server.core.storage.StorageHashMapBuilder;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.iotdb.IoTDBClient;
+
+public class IoTDBEventQueryDAO implements IEventQueryDAO {
+    private final IoTDBClient client;
+    private final StorageHashMapBuilder<Event> storageBuilder = new Event.Builder();
+
+    public IoTDBEventQueryDAO(IoTDBClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public Events queryEvents(EventQueryCondition condition) throws Exception {
+        // This method maybe have poor efficiency. It queries all data which meets a condition without select function.
+        // https://github.com/apache/iotdb/discussions/3888
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(Event.INDEX_NAME);
+        query = client.addQueryAsterisk(Event.INDEX_NAME, query);
+        query = whereSQL(condition, query);
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(Event.INDEX_NAME, query.toString(), storageBuilder);
+        final Events events = new Events();
+        int limitCount = 0;
+        PaginationUtils.Page page = PaginationUtils.INSTANCE.exchange(condition.getPaging());
+        for (int i = 0; i < storageDataList.size(); i++) {
+            if (i >= page.getFrom() && limitCount < page.getLimit()) {
+                limitCount++;
+                Event event = (Event) storageDataList.get(i);
+                org.apache.skywalking.oap.server.core.query.type.event.Event resultEvent = parseEvent(event);
+                events.getEvents().add(resultEvent);
+            }
+        }
+        events.setTotal(storageDataList.size());
+        // resort by self, because of the select query result order by time.
+        final Order order = Objects.isNull(condition.getOrder()) ? Order.DES : condition.getOrder();
+        if (Order.DES.equals(order)) {
+            events.getEvents().sort(
+                    (org.apache.skywalking.oap.server.core.query.type.event.Event e1,
+                     org.apache.skywalking.oap.server.core.query.type.event.Event e2)
+                            -> Long.compare(e2.getStartTime(), e1.getStartTime()));
+        } else {
+            events.getEvents().sort(

Review comment:
       *NULL_DEREFERENCE:*  object returned by `getEvents(events)` could be null and is dereferenced at line 77.
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)

##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/iotdb/query/IoTDBMetadataQueryDAO.java
##########
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.storage.plugin.iotdb.query;
+
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.util.HashMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.analysis.NodeType;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.manual.endpoint.EndpointTraffic;
+import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
+import org.apache.skywalking.oap.server.core.analysis.manual.service.ServiceTraffic;
+import org.apache.skywalking.oap.server.core.query.enumeration.Language;
+import org.apache.skywalking.oap.server.core.query.type.Attribute;
+import org.apache.skywalking.oap.server.core.query.type.Database;
+import org.apache.skywalking.oap.server.core.query.type.Endpoint;
+import org.apache.skywalking.oap.server.core.query.type.Service;
+import org.apache.skywalking.oap.server.core.query.type.ServiceInstance;
+import org.apache.skywalking.oap.server.core.storage.StorageData;
+import org.apache.skywalking.oap.server.core.storage.StorageHashMapBuilder;
+import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.iotdb.IoTDBClient;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class IoTDBMetadataQueryDAO implements IMetadataQueryDAO {
+    private static final Gson GSON = new Gson();
+    private final IoTDBClient client;
+    private final StorageHashMapBuilder<ServiceTraffic> serviceBuilder = new ServiceTraffic.Builder();
+    private final StorageHashMapBuilder<EndpointTraffic> endpointBuilder = new EndpointTraffic.Builder();
+    private final StorageHashMapBuilder<InstanceTraffic> instanceBuilder = new InstanceTraffic.Builder();
+
+    public IoTDBMetadataQueryDAO(IoTDBClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public List<Service> getAllServices(String group) throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(ServiceTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.NODE_TYPE_IDX, String.valueOf(NodeType.Normal.value()));
+        if (StringUtil.isNotEmpty(group)) {
+            indexAndValueMap.put(IoTDBClient.GROUP_IDX, group);
+        }
+        query = client.addQueryIndexValue(ServiceTraffic.INDEX_NAME, query, indexAndValueMap);
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(ServiceTraffic.INDEX_NAME, query.toString(), serviceBuilder);
+        List<Service> serviceList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> serviceList.add(buildService((ServiceTraffic) storageData)));
+        return serviceList;
+    }
+
+    @Override
+    public List<Service> getAllBrowserServices() throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(ServiceTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.NODE_TYPE_IDX, String.valueOf(NodeType.Browser.value()));
+        query = client.addQueryIndexValue(ServiceTraffic.INDEX_NAME, query, indexAndValueMap);
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(ServiceTraffic.INDEX_NAME, query.toString(), serviceBuilder);
+        List<Service> serviceList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> serviceList.add(buildService((ServiceTraffic) storageData)));
+        return serviceList;
+    }
+
+    @Override
+    public List<Database> getAllDatabases() throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(ServiceTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.NODE_TYPE_IDX, String.valueOf(NodeType.Database.value()));
+        query = client.addQueryIndexValue(ServiceTraffic.INDEX_NAME, query, indexAndValueMap);
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(ServiceTraffic.INDEX_NAME, query.toString(), serviceBuilder);
+        List<Database> databaseList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> {
+            ServiceTraffic serviceTraffic = (ServiceTraffic) storageData;
+            Database database = new Database();
+            database.setId(serviceTraffic.id());
+            database.setName(serviceTraffic.getName());
+            databaseList.add(database);
+        });
+        return databaseList;
+    }
+
+    @Override
+    public List<Service> searchServices(String keyword) throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(ServiceTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.NODE_TYPE_IDX, String.valueOf(NodeType.Normal.value()));
+        query = client.addQueryIndexValue(ServiceTraffic.INDEX_NAME, query, indexAndValueMap);
+        if (!Strings.isNullOrEmpty(keyword)) {
+            query.append(" where ").append(ServiceTraffic.NAME).append(" like '%").append(keyword).append("%'");
+        }
+        query.append(IoTDBClient.ALIGN_BY_DEVICE);
+        List<? super StorageData> storageDataList = client.filterQuery(ServiceTraffic.INDEX_NAME, query.toString(), serviceBuilder);
+        List<Service> serviceList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> serviceList.add(buildService((ServiceTraffic) storageData)));
+        return serviceList;
+    }
+
+    @Override
+    public Service searchService(String serviceCode) throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(ServiceTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.NODE_TYPE_IDX, String.valueOf(NodeType.Normal.value()));
+        query = client.addQueryIndexValue(ServiceTraffic.INDEX_NAME, query, indexAndValueMap);
+        query.append(" where ").append(ServiceTraffic.NAME).append(" = \"").append(serviceCode).append("\"")
+                .append(IoTDBClient.ALIGN_BY_DEVICE);
+        List<? super StorageData> storageDataList = client.filterQuery(ServiceTraffic.INDEX_NAME, query.toString(), serviceBuilder);
+        return buildService((ServiceTraffic) storageDataList.get(0));
+    }
+
+    @Override
+    public List<Endpoint> searchEndpoint(String keyword, String serviceId, int limit) throws IOException {
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(EndpointTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.SERVICE_ID_IDX, serviceId);
+        query = client.addQueryIndexValue(EndpointTraffic.INDEX_NAME, query, indexAndValueMap);
+        if (!Strings.isNullOrEmpty(keyword)) {
+            query.append(" where ").append(EndpointTraffic.NAME).append(" like '%").append(keyword).append("%'");
+        }
+        query.append(" limit ").append(limit).append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(EndpointTraffic.INDEX_NAME, query.toString(), endpointBuilder);
+        List<Endpoint> endpointList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> {
+            EndpointTraffic endpointTraffic = (EndpointTraffic) storageData;
+            Endpoint endpoint = new Endpoint();
+            endpoint.setId(endpointTraffic.id());
+            endpoint.setName(endpointTraffic.getName());
+            endpointList.add(endpoint);
+        });
+        return endpointList;
+    }
+
+    @Override
+    public List<ServiceInstance> getServiceInstances(long startTimestamp, long endTimestamp, String serviceId) throws IOException {
+        final long minuteTimeBucket = TimeBucket.getMinuteTimeBucket(startTimestamp);
+        StringBuilder query = new StringBuilder();
+        query.append("select * from ").append(client.getStorageGroup()).append(IoTDBClient.DOT).append(InstanceTraffic.INDEX_NAME);
+        Map<String, String> indexAndValueMap = new HashMap<>();
+        indexAndValueMap.put(IoTDBClient.SERVICE_ID_IDX, serviceId);
+        query = client.addQueryIndexValue(InstanceTraffic.INDEX_NAME, query, indexAndValueMap);
+        query.append(" where ").append(InstanceTraffic.LAST_PING_TIME_BUCKET).append(" >= ").append(minuteTimeBucket)
+                .append(IoTDBClient.ALIGN_BY_DEVICE);
+
+        List<? super StorageData> storageDataList = client.filterQuery(InstanceTraffic.INDEX_NAME, query.toString(), instanceBuilder);
+        List<ServiceInstance> serviceInstanceList = new ArrayList<>(storageDataList.size());
+        storageDataList.forEach(storageData -> {
+            InstanceTraffic instanceTraffic = (InstanceTraffic) storageData;
+            ServiceInstance serviceInstance = new ServiceInstance();
+            serviceInstance.setId(instanceTraffic.id());
+            serviceInstance.setName(instanceTraffic.getName());
+            serviceInstance.setInstanceUUID(serviceInstance.getId());
+
+            JsonObject properties = instanceTraffic.getProperties();
+            if (!properties.isJsonNull()) {
+                for (Map.Entry<String, JsonElement> property : properties.entrySet()) {
+                    String key = property.getKey();
+                    String value = property.getValue().getAsString();
+                    if (key.equals(InstanceTraffic.PropertyUtil.LANGUAGE)) {
+                        serviceInstance.setLanguage(Language.value(value));
+                    } else {
+                        serviceInstance.getAttributes().add(new Attribute(key, value));

Review comment:
       *NULL_DEREFERENCE:*  object returned by `getAttributes(serviceInstance)` could be null and is dereferenced at line 197.
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)

##########
File path: oap-server/server-storage-plugin/storage-iotdb-plugin/pom.xml
##########
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>server-storage-plugin</artifactId>
+        <groupId>org.apache.skywalking</groupId>
+        <version>8.8.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>storage-iotdb-plugin</artifactId>
+    <packaging>jar</packaging>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>server-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>library-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>

Review comment:
       *Critical OSS Vulnerability:*
   ### pkg:maven/org.apache.iotdb/iotdb-session@0.12.2
   12 Critical, 2 Severe, 0 Moderate, 0 Unknown vulnerabilities have been found across 1 dependencies
   
   <details>
     <summary><b>Components</b></summary><br/>
     <ul>
         <details>
           <summary><b>pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@8.5.46</b></summary>
           <ul>
     <details>
       <summary><b>CRITICAL Vulnerabilities (12)</b></summary><br/>
   <ul>
   <details>
               <summary>CVE-2020-1938</summary>
   
   > #### [CVE-2020-1938] When using the Apache JServ Protocol (AJP), care must be taken when trusting inc...
   > When using the Apache JServ Protocol (AJP), care must be taken when trusting incoming connections to Apache Tomcat. Tomcat treats AJP connections as having higher trust than, for example, a similar HTTP connection. If such connections are available to an attacker, they can be exploited in ways that may be surprising. In Apache Tomcat 9.0.0.M1 to 9.0.0.30, 8.5.0 to 8.5.50 and 7.0.0 to 7.0.99, Tomcat shipped with an AJP Connector enabled by default that listened on all configured IP addresses. It was expected (and recommended in the security guide) that this Connector would be disabled if not required. This vulnerability report identified a mechanism that allowed: - returning arbitrary files from anywhere in the web application - processing any file in the web application as a JSP Further, if the web application allowed file upload and stored those files within the web application (or the attacker was able to control the content of the web application by some other means) then thi
 s, along with the ability to process a file as a JSP, made remote code execution possible. It is important to note that mitigation is only required if an AJP port is accessible to untrusted users. Users wishing to take a defence-in-depth approach and block the vector that permits returning arbitrary files and execution as JSP may upgrade to Apache Tomcat 9.0.31, 8.5.51 or 7.0.100 or later. A number of changes were made to the default AJP Connector configuration in 9.0.31 to harden the default configuration. It is likely that users upgrading to 9.0.31, 8.5.51 or 7.0.100 or later will need to make small changes to their configurations.
   >
   > **CVSS Score:** 9.8
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
   
   </details>
   <details>
               <summary>CVE-2021-30640</summary>
   
   > #### [CVE-2021-30640] A vulnerability in the JNDI Realm of Apache Tomcat allows an attacker to authent...
   > A vulnerability in the JNDI Realm of Apache Tomcat allows an attacker to authenticate using variations of a valid user name and/or to bypass some of the protection provided by the LockOut Realm. This issue affects Apache Tomcat 10.0.0-M1 to 10.0.5; 9.0.0.M1 to 9.0.45; 8.5.0 to 8.5.65.
   >
   > **CVSS Score:** 8.2
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
   
   </details>
   <details>
               <summary>CVE-2020-13935</summary>
   
   > #### [CVE-2020-13935] The payload length in a WebSocket frame was not correctly validated in Apache To...
   > The payload length in a WebSocket frame was not correctly validated in Apache Tomcat 10.0.0-M1 to 10.0.0-M6, 9.0.0.M1 to 9.0.36, 8.5.0 to 8.5.56 and 7.0.27 to 7.0.104. Invalid payload lengths could trigger an infinite loop. Multiple requests with invalid payload lengths could lead to a denial of service.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
   
   </details>
   <details>
               <summary>CVE-2019-17563</summary>
   
   > #### [CVE-2019-17563] When using FORM authentication with Apache Tomcat 9.0.0.M1 to 9.0.29, 8.5.0 to 8...
   > When using FORM authentication with Apache Tomcat 9.0.0.M1 to 9.0.29, 8.5.0 to 8.5.49 and 7.0.0 to 7.0.98 there was a narrow window where an attacker could perform a session fixation attack. The window was considered too narrow for an exploit to be practical but, erring on the side of caution, this issue has been treated as a security vulnerability.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
   
   </details>
   <details>
               <summary>CVE-2020-13934</summary>
   
   > #### [CVE-2020-13934] An h2c direct connection to Apache Tomcat 10.0.0-M1 to 10.0.0-M6, 9.0.0.M5 to 9....
   > An h2c direct connection to Apache Tomcat 10.0.0-M1 to 10.0.0-M6, 9.0.0.M5 to 9.0.36 and 8.5.1 to 8.5.56 did not release the HTTP/1.1 processor after the upgrade to HTTP/2. If a sufficient number of such requests were made, an OutOfMemoryException could occur leading to a denial of service.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
   
   </details>
   <details>
               <summary>CVE-2020-17527</summary>
   
   > #### [CVE-2020-17527] While investigating bug 64830 it was discovered that Apache Tomcat 10.0.0-M1 to ...
   > While investigating bug 64830 it was discovered that Apache Tomcat 10.0.0-M1 to 10.0.0-M9, 9.0.0-M1 to 9.0.39 and 8.5.0 to 8.5.59 could re-use an HTTP request header value from the previous stream received on an HTTP/2 connection for the request associated with the subsequent stream. While this would most likely lead to an error and the closure of the HTTP/2 connection, it is possible that information could leak between requests.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
   
   </details>
   <details>
               <summary>CVE-2020-11996</summary>
   
   > #### [CVE-2020-11996] A specially crafted sequence of HTTP/2 requests sent to Apache Tomcat 10.0.0-M1 ...
   > A specially crafted sequence of HTTP/2 requests sent to Apache Tomcat 10.0.0-M1 to 10.0.0-M5, 9.0.0.M1 to 9.0.35 and 8.5.0 to 8.5.55 could trigger high CPU usage for several seconds. If a sufficient number of such requests were made on concurrent HTTP/2 connections, the server could become unresponsive.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
   
   </details>
   <details>
               <summary>CVE-2021-25122</summary>
   
   > #### [CVE-2021-25122] When responding to new h2c connection requests, Apache Tomcat versions 10.0.0-M1...
   > When responding to new h2c connection requests, Apache Tomcat versions 10.0.0-M1 to 10.0.0, 9.0.0.M1 to 9.0.41 and 8.5.0 to 8.5.61 could duplicate request headers and a limited amount of request body from one request to another meaning user A and user B could both see the results of user A&#39;s request.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
   
   </details>
   <details>
               <summary>CVE-2021-24122</summary>
   
   > #### [CVE-2021-24122] When serving resources from a network location using the NTFS file system, Apach...
   > When serving resources from a network location using the NTFS file system, Apache Tomcat versions 10.0.0-M1 to 10.0.0-M9, 9.0.0.M1 to 9.0.39, 8.5.0 to 8.5.59 and 7.0.0 to 7.0.106 were susceptible to JSP source code disclosure in some configurations. The root cause was the unexpected behaviour of the JRE API File.getCanonicalPath() which in turn was caused by the inconsistent behaviour of the Windows API (FindFirstFileW) in some circumstances.
   >
   > **CVSS Score:** 7.5
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
   
   </details>
   <details>
               <summary>CVE-2020-9484</summary>
   
   > #### [CVE-2020-9484] When using Apache Tomcat versions 10.0.0-M1 to 10.0.0-M4, 9.0.0.M1 to 9.0.34, 8....
   > When using Apache Tomcat versions 10.0.0-M1 to 10.0.0-M4, 9.0.0.M1 to 9.0.34, 8.5.0 to 8.5.54 and 7.0.0 to 7.0.103 if a) an attacker is able to control the contents and name of a file on the server; and b) the server is configured to use the PersistenceManager with a FileStore; and c) the PersistenceManager is configured with sessionAttributeValueClassNameFilter=&quot;null&quot; (the default unless a SecurityManager is used) or a sufficiently lax filter to allow the attacker provided object to be deserialized; and d) the attacker knows the relative file path from the storage location used by FileStore to the file the attacker has control over; then, using a specifically crafted request, the attacker will be able to trigger remote code execution via deserialization of the file under their control. Note that all of conditions a) to d) must be true for the attack to succeed.
   >
   > **CVSS Score:** 7
   >
   > **CVSS Vector:** CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
   
   </details>
   <details>
               <summary>CVE-2021-25329</summary>
   
   > #### [CVE-2021-25329] The fix for CVE-2020-9484 was incomplete. When using Apache Tomcat 10.0.0-M1 to ...
   > The fix for CVE-2020-9484 was incomplete. When using Apache Tomcat 10.0.0-M1 to 10.0.0, 9.0.0.M1 to 9.0.41, 8.5.0 to 8.5.61 or 7.0.0. to 7.0.107 with a configuration edge case that was highly unlikely to be used, the Tomcat instance was still vulnerable to CVE-2020-9494. Note that both the previously published prerequisites for CVE-2020-9484 and the previously published mitigations for CVE-2020-9484 also apply to this issue.
   >
   > **CVSS Score:** 7
   >
   > **CVSS Vector:** CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
   
   </details>
   <details>
               <summary>CVE-2019-12418</summary>
   
   > #### [CVE-2019-12418] When Apache Tomcat 9.0.0.M1 to 9.0.28, 8.5.0 to 8.5.47, 7.0.0 and 7.0.97 is conf...
   > When Apache Tomcat 9.0.0.M1 to 9.0.28, 8.5.0 to 8.5.47, 7.0.0 and 7.0.97 is configured with the JMX Remote Lifecycle Listener, a local attacker without access to the Tomcat process or configuration files is able to manipulate the RMI registry to perform a man-in-the-middle attack to capture user names and passwords used to access the JMX interface. The attacker can then use these credentials to access the JMX interface and gain complete control over the Tomcat instance.
   >
   > **CVSS Score:** 7
   >
   > **CVSS Vector:** CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
   
   </details>
   </ul>
       </details>
     <details>
       <summary><b>SEVERE Vulnerabilities (2)</b></summary><br/>
   <ul>
   <details>
               <summary>CVE-2021-33037</summary>
   
   > #### [CVE-2021-33037] Apache Tomcat 10.0.0-M1 to 10.0.6, 9.0.0.M1 to 9.0.46 and 8.5.0 to 8.5.66 did no...
   > Apache Tomcat 10.0.0-M1 to 10.0.6, 9.0.0.M1 to 9.0.46 and 8.5.0 to 8.5.66 did not correctly parse the HTTP transfer-encoding request header in some circumstances leading to the possibility to request smuggling when used with a reverse proxy. Specifically: - Tomcat incorrectly ignored the transfer encoding header if the client declared it would only accept an HTTP/1.0 response; - Tomcat honoured the identify encoding; and - Tomcat did not ensure that, if present, the chunked encoding was the final encoding.
   >
   > **CVSS Score:** 5.3
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
   
   </details>
   <details>
               <summary>CVE-2020-13943</summary>
   
   > #### [CVE-2020-13943] If an HTTP/2 client connecting to Apache Tomcat 10.0.0-M1 to 10.0.0-M7, 9.0.0.M1...
   > If an HTTP/2 client connecting to Apache Tomcat 10.0.0-M1 to 10.0.0-M7, 9.0.0.M1 to 9.0.37 or 8.5.0 to 8.5.57 exceeded the agreed maximum number of concurrent streams for a connection (in violation of the HTTP/2 protocol), it was possible that a subsequent request made on that connection could contain HTTP headers - including HTTP/2 pseudo headers - from a previous request rather than the intended headers. This could lead to users seeing responses for unexpected resources.
   >
   > **CVSS Score:** 4.3
   >
   > **CVSS Vector:** CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
   
   </details>
   </ul>
       </details>
           </ul>
         </details>
     </ul>
   </details>
   (at-me [in a reply](https://help.sonatype.com/lift/talking-to-lift) with `help` or `ignore`)




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@skywalking.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org