Showing
14 changed files
with
509 additions
and
30 deletions
@@ -90,7 +90,7 @@ public class AlarmController extends BaseController { | @@ -90,7 +90,7 @@ public class AlarmController extends BaseController { | ||
90 | checkEntity(alarm.getId(), alarm, Resource.ALARM); | 90 | checkEntity(alarm.getId(), alarm, Resource.ALARM); |
91 | 91 | ||
92 | Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); | 92 | Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); |
93 | - logEntityAction(savedAlarm.getId(), savedAlarm, | 93 | + logEntityAction(savedAlarm.getOriginator(), savedAlarm, |
94 | getCurrentUser().getCustomerId(), | 94 | getCurrentUser().getCustomerId(), |
95 | alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); | 95 | alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
96 | return savedAlarm; | 96 | return savedAlarm; |
@@ -126,7 +126,7 @@ public class AlarmController extends BaseController { | @@ -126,7 +126,7 @@ public class AlarmController extends BaseController { | ||
126 | long ackTs = System.currentTimeMillis(); | 126 | long ackTs = System.currentTimeMillis(); |
127 | alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); | 127 | alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); |
128 | alarm.setAckTs(ackTs); | 128 | alarm.setAckTs(ackTs); |
129 | - logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); | 129 | + logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); |
130 | } catch (Exception e) { | 130 | } catch (Exception e) { |
131 | throw handleException(e); | 131 | throw handleException(e); |
132 | } | 132 | } |
@@ -143,7 +143,7 @@ public class AlarmController extends BaseController { | @@ -143,7 +143,7 @@ public class AlarmController extends BaseController { | ||
143 | long clearTs = System.currentTimeMillis(); | 143 | long clearTs = System.currentTimeMillis(); |
144 | alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); | 144 | alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); |
145 | alarm.setClearTs(clearTs); | 145 | alarm.setClearTs(clearTs); |
146 | - logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); | 146 | + logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); |
147 | } catch (Exception e) { | 147 | } catch (Exception e) { |
148 | throw handleException(e); | 148 | throw handleException(e); |
149 | } | 149 | } |
@@ -22,6 +22,7 @@ import org.apache.commons.lang3.RandomStringUtils; | @@ -22,6 +22,7 @@ import org.apache.commons.lang3.RandomStringUtils; | ||
22 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | 22 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
23 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 23 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
24 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 24 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
25 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
25 | import org.junit.After; | 26 | import org.junit.After; |
26 | import org.junit.Assert; | 27 | import org.junit.Assert; |
27 | import org.junit.Before; | 28 | import org.junit.Before; |
@@ -424,7 +425,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -424,7 +425,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
424 | assertNotNull(accessToken); | 425 | assertNotNull(accessToken); |
425 | 426 | ||
426 | String clientId = MqttAsyncClient.generateClientId(); | 427 | String clientId = MqttAsyncClient.generateClientId(); |
427 | - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId); | 428 | + MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); |
428 | 429 | ||
429 | MqttConnectOptions options = new MqttConnectOptions(); | 430 | MqttConnectOptions options = new MqttConnectOptions(); |
430 | options.setUserName(accessToken); | 431 | options.setUserName(accessToken); |
@@ -466,7 +467,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -466,7 +467,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
466 | assertNotNull(accessToken); | 467 | assertNotNull(accessToken); |
467 | 468 | ||
468 | String clientId = MqttAsyncClient.generateClientId(); | 469 | String clientId = MqttAsyncClient.generateClientId(); |
469 | - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId); | 470 | + MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); |
470 | 471 | ||
471 | MqttConnectOptions options = new MqttConnectOptions(); | 472 | MqttConnectOptions options = new MqttConnectOptions(); |
472 | options.setUserName(accessToken); | 473 | options.setUserName(accessToken); |
@@ -21,6 +21,7 @@ import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | @@ -21,6 +21,7 @@ import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
21 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 21 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
22 | import org.eclipse.paho.client.mqttv3.MqttException; | 22 | import org.eclipse.paho.client.mqttv3.MqttException; |
23 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 23 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
24 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
24 | import org.junit.Assert; | 25 | import org.junit.Assert; |
25 | import org.springframework.util.StringUtils; | 26 | import org.springframework.util.StringUtils; |
26 | import org.thingsboard.server.common.data.Device; | 27 | import org.thingsboard.server.common.data.Device; |
@@ -128,7 +129,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -128,7 +129,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
128 | 129 | ||
129 | protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException { | 130 | protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException { |
130 | String clientId = MqttAsyncClient.generateClientId(); | 131 | String clientId = MqttAsyncClient.generateClientId(); |
131 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | 132 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); |
132 | 133 | ||
133 | MqttConnectOptions options = new MqttConnectOptions(); | 134 | MqttConnectOptions options = new MqttConnectOptions(); |
134 | options.setUserName(accessToken); | 135 | options.setUserName(accessToken); |
@@ -18,6 +18,7 @@ package org.thingsboard.server.mqtt.claim; | @@ -18,6 +18,7 @@ package org.thingsboard.server.mqtt.claim; | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | import org.junit.After; | 19 | import org.junit.After; |
20 | import org.junit.Before; | 20 | import org.junit.Before; |
21 | +import org.junit.Ignore; | ||
21 | import org.junit.Test; | 22 | import org.junit.Test; |
22 | import org.thingsboard.server.common.data.TransportPayloadType; | 23 | import org.thingsboard.server.common.data.TransportPayloadType; |
23 | 24 | ||
@@ -51,6 +52,7 @@ public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimD | @@ -51,6 +52,7 @@ public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimD | ||
51 | } | 52 | } |
52 | 53 | ||
53 | @Test | 54 | @Test |
55 | + @Ignore | ||
54 | public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | 56 | public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
55 | processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true); | 57 | processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true); |
56 | } | 58 | } |
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; | @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; | ||
19 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | 19 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
20 | import org.junit.After; | 20 | import org.junit.After; |
21 | import org.junit.Before; | 21 | import org.junit.Before; |
22 | +import org.junit.Ignore; | ||
22 | import org.junit.Test; | 23 | import org.junit.Test; |
23 | import org.thingsboard.server.common.data.TransportPayloadType; | 24 | import org.thingsboard.server.common.data.TransportPayloadType; |
24 | import org.thingsboard.server.gen.transport.TransportApiProtos; | 25 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
@@ -36,21 +37,25 @@ public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaim | @@ -36,21 +37,25 @@ public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaim | ||
36 | public void afterTest() throws Exception { super.afterTest(); } | 37 | public void afterTest() throws Exception { super.afterTest(); } |
37 | 38 | ||
38 | @Test | 39 | @Test |
40 | + @Ignore | ||
39 | public void testClaimingDevice() throws Exception { | 41 | public void testClaimingDevice() throws Exception { |
40 | processTestClaimingDevice(false); | 42 | processTestClaimingDevice(false); |
41 | } | 43 | } |
42 | 44 | ||
43 | @Test | 45 | @Test |
46 | + @Ignore | ||
44 | public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | 47 | public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
45 | processTestClaimingDevice(true); | 48 | processTestClaimingDevice(true); |
46 | } | 49 | } |
47 | 50 | ||
48 | @Test | 51 | @Test |
52 | + @Ignore | ||
49 | public void testGatewayClaimingDevice() throws Exception { | 53 | public void testGatewayClaimingDevice() throws Exception { |
50 | processTestGatewayClaimingDevice("Test claiming gateway device Proto", false); | 54 | processTestGatewayClaimingDevice("Test claiming gateway device Proto", false); |
51 | } | 55 | } |
52 | 56 | ||
53 | @Test | 57 | @Test |
58 | + @Ignore | ||
54 | public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | 59 | public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
55 | processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true); | 60 | processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true); |
56 | } | 61 | } |
@@ -22,6 +22,7 @@ import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | @@ -22,6 +22,7 @@ import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
22 | import org.eclipse.paho.client.mqttv3.MqttCallback; | 22 | import org.eclipse.paho.client.mqttv3.MqttCallback; |
23 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 23 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
24 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 24 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
25 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
25 | import org.junit.After; | 26 | import org.junit.After; |
26 | import org.junit.Before; | 27 | import org.junit.Before; |
27 | import org.junit.Test; | 28 | import org.junit.Test; |
@@ -228,7 +229,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt | @@ -228,7 +229,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt | ||
228 | // @Test - Unstable | 229 | // @Test - Unstable |
229 | public void testMqttQoSLevel() throws Exception { | 230 | public void testMqttQoSLevel() throws Exception { |
230 | String clientId = MqttAsyncClient.generateClientId(); | 231 | String clientId = MqttAsyncClient.generateClientId(); |
231 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | 232 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); |
232 | 233 | ||
233 | MqttConnectOptions options = new MqttConnectOptions(); | 234 | MqttConnectOptions options = new MqttConnectOptions(); |
234 | options.setUserName(accessToken); | 235 | options.setUserName(accessToken); |
@@ -25,9 +25,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; | @@ -25,9 +25,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
25 | include = JsonTypeInfo.As.PROPERTY, | 25 | include = JsonTypeInfo.As.PROPERTY, |
26 | property = "type") | 26 | property = "type") |
27 | @JsonSubTypes({ | 27 | @JsonSubTypes({ |
28 | - @JsonSubTypes.Type(value = SimpleAlarmConditionSpec.class, name = "ANY_TIME"), | ||
29 | - @JsonSubTypes.Type(value = DurationAlarmConditionSpec.class, name = "SPECIFIC_TIME"), | ||
30 | - @JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "CUSTOM")}) | 28 | + @JsonSubTypes.Type(value = AnyTimeSchedule.class, name = "ANY_TIME"), |
29 | + @JsonSubTypes.Type(value = SpecificTimeSchedule.class, name = "SPECIFIC_TIME"), | ||
30 | + @JsonSubTypes.Type(value = CustomTimeSchedule.class, name = "CUSTOM")}) | ||
31 | public interface AlarmSchedule { | 31 | public interface AlarmSchedule { |
32 | 32 | ||
33 | AlarmScheduleType getType(); | 33 | AlarmScheduleType getType(); |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, software | ||
13 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
14 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
15 | + * See the License for the specific language governing permissions and | ||
16 | + * limitations under the License. | ||
17 | + */ | ||
18 | +package org.apache.cassandra.io.sstable; | ||
19 | + | ||
20 | +import java.io.File; | ||
21 | +import java.io.IOError; | ||
22 | +import java.io.IOException; | ||
23 | +import java.util.*; | ||
24 | +import java.util.regex.Pattern; | ||
25 | + | ||
26 | +import com.google.common.annotations.VisibleForTesting; | ||
27 | +import com.google.common.base.CharMatcher; | ||
28 | +import com.google.common.base.Objects; | ||
29 | + | ||
30 | +import org.apache.cassandra.db.Directories; | ||
31 | +import org.apache.cassandra.io.sstable.format.SSTableFormat; | ||
32 | +import org.apache.cassandra.io.sstable.format.Version; | ||
33 | +import org.apache.cassandra.io.sstable.metadata.IMetadataSerializer; | ||
34 | +import org.apache.cassandra.io.sstable.metadata.LegacyMetadataSerializer; | ||
35 | +import org.apache.cassandra.io.sstable.metadata.MetadataSerializer; | ||
36 | +import org.apache.cassandra.utils.Pair; | ||
37 | + | ||
38 | +import static org.apache.cassandra.io.sstable.Component.separator; | ||
39 | + | ||
40 | +/** | ||
41 | + * A SSTable is described by the keyspace and column family it contains data | ||
42 | + * for, a generation (where higher generations contain more recent data) and | ||
43 | + * an alphabetic version string. | ||
44 | + * | ||
45 | + * A descriptor can be marked as temporary, which influences generated filenames. | ||
46 | + */ | ||
47 | +public class Descriptor | ||
48 | +{ | ||
49 | + public static String TMP_EXT = ".tmp"; | ||
50 | + | ||
51 | + /** canonicalized path to the directory where SSTable resides */ | ||
52 | + public final File directory; | ||
53 | + /** version has the following format: <code>[a-z]+</code> */ | ||
54 | + public final Version version; | ||
55 | + public final String ksname; | ||
56 | + public final String cfname; | ||
57 | + public final int generation; | ||
58 | + public final SSTableFormat.Type formatType; | ||
59 | + /** digest component - might be {@code null} for old, legacy sstables */ | ||
60 | + public final Component digestComponent; | ||
61 | + private final int hashCode; | ||
62 | + | ||
63 | + /** | ||
64 | + * A descriptor that assumes CURRENT_VERSION. | ||
65 | + */ | ||
66 | + @VisibleForTesting | ||
67 | + public Descriptor(File directory, String ksname, String cfname, int generation) | ||
68 | + { | ||
69 | + this(SSTableFormat.Type.current().info.getLatestVersion(), directory, ksname, cfname, generation, SSTableFormat.Type.current(), null); | ||
70 | + } | ||
71 | + | ||
72 | + /** | ||
73 | + * Constructor for sstable writers only. | ||
74 | + */ | ||
75 | + public Descriptor(File directory, String ksname, String cfname, int generation, SSTableFormat.Type formatType) | ||
76 | + { | ||
77 | + this(formatType.info.getLatestVersion(), directory, ksname, cfname, generation, formatType, Component.digestFor(formatType.info.getLatestVersion().uncompressedChecksumType())); | ||
78 | + } | ||
79 | + | ||
80 | + @VisibleForTesting | ||
81 | + public Descriptor(String version, File directory, String ksname, String cfname, int generation, SSTableFormat.Type formatType) | ||
82 | + { | ||
83 | + this(formatType.info.getVersion(version), directory, ksname, cfname, generation, formatType, Component.digestFor(formatType.info.getLatestVersion().uncompressedChecksumType())); | ||
84 | + } | ||
85 | + | ||
86 | + public Descriptor(Version version, File directory, String ksname, String cfname, int generation, SSTableFormat.Type formatType, Component digestComponent) | ||
87 | + { | ||
88 | + assert version != null && directory != null && ksname != null && cfname != null && formatType.info.getLatestVersion().getClass().equals(version.getClass()); | ||
89 | + this.version = version; | ||
90 | + try | ||
91 | + { | ||
92 | + this.directory = directory.getCanonicalFile(); | ||
93 | + } | ||
94 | + catch (IOException e) | ||
95 | + { | ||
96 | + throw new IOError(e); | ||
97 | + } | ||
98 | + this.ksname = ksname; | ||
99 | + this.cfname = cfname; | ||
100 | + this.generation = generation; | ||
101 | + this.formatType = formatType; | ||
102 | + this.digestComponent = digestComponent; | ||
103 | + | ||
104 | + hashCode = Objects.hashCode(version, this.directory, generation, ksname, cfname, formatType); | ||
105 | + } | ||
106 | + | ||
107 | + public Descriptor withGeneration(int newGeneration) | ||
108 | + { | ||
109 | + return new Descriptor(version, directory, ksname, cfname, newGeneration, formatType, digestComponent); | ||
110 | + } | ||
111 | + | ||
112 | + public Descriptor withFormatType(SSTableFormat.Type newType) | ||
113 | + { | ||
114 | + return new Descriptor(newType.info.getLatestVersion(), directory, ksname, cfname, generation, newType, digestComponent); | ||
115 | + } | ||
116 | + | ||
117 | + public Descriptor withDigestComponent(Component newDigestComponent) | ||
118 | + { | ||
119 | + return new Descriptor(version, directory, ksname, cfname, generation, formatType, newDigestComponent); | ||
120 | + } | ||
121 | + | ||
122 | + public String tmpFilenameFor(Component component) | ||
123 | + { | ||
124 | + return filenameFor(component) + TMP_EXT; | ||
125 | + } | ||
126 | + | ||
127 | + public String filenameFor(Component component) | ||
128 | + { | ||
129 | + return baseFilename() + separator + component.name(); | ||
130 | + } | ||
131 | + | ||
132 | + public String baseFilename() | ||
133 | + { | ||
134 | + StringBuilder buff = new StringBuilder(); | ||
135 | + buff.append(directory).append(File.separatorChar); | ||
136 | + appendFileName(buff); | ||
137 | + return buff.toString(); | ||
138 | + } | ||
139 | + | ||
140 | + private void appendFileName(StringBuilder buff) | ||
141 | + { | ||
142 | + if (!version.hasNewFileName()) | ||
143 | + { | ||
144 | + buff.append(ksname).append(separator); | ||
145 | + buff.append(cfname).append(separator); | ||
146 | + } | ||
147 | + buff.append(version).append(separator); | ||
148 | + buff.append(generation); | ||
149 | + if (formatType != SSTableFormat.Type.LEGACY) | ||
150 | + buff.append(separator).append(formatType.name); | ||
151 | + } | ||
152 | + | ||
153 | + public String relativeFilenameFor(Component component) | ||
154 | + { | ||
155 | + final StringBuilder buff = new StringBuilder(); | ||
156 | + appendFileName(buff); | ||
157 | + buff.append(separator).append(component.name()); | ||
158 | + return buff.toString(); | ||
159 | + } | ||
160 | + | ||
161 | + public SSTableFormat getFormat() | ||
162 | + { | ||
163 | + return formatType.info; | ||
164 | + } | ||
165 | + | ||
166 | + /** Return any temporary files found in the directory */ | ||
167 | + public List<File> getTemporaryFiles() | ||
168 | + { | ||
169 | + List<File> ret = new ArrayList<>(); | ||
170 | + File[] tmpFiles = directory.listFiles((dir, name) -> | ||
171 | + name.endsWith(Descriptor.TMP_EXT)); | ||
172 | + | ||
173 | + for (File tmpFile : tmpFiles) | ||
174 | + ret.add(tmpFile); | ||
175 | + | ||
176 | + return ret; | ||
177 | + } | ||
178 | + | ||
179 | + /** | ||
180 | + * Files obsoleted by CASSANDRA-7066 : temporary files and compactions_in_progress. We support | ||
181 | + * versions 2.1 (ka) and 2.2 (la). | ||
182 | + * Temporary files have tmp- or tmplink- at the beginning for 2.2 sstables or after ks-cf- for 2.1 sstables | ||
183 | + */ | ||
184 | + | ||
185 | + private final static String LEGACY_COMP_IN_PROG_REGEX_STR = "^compactions_in_progress(\\-[\\d,a-f]{32})?$"; | ||
186 | + private final static Pattern LEGACY_COMP_IN_PROG_REGEX = Pattern.compile(LEGACY_COMP_IN_PROG_REGEX_STR); | ||
187 | + private final static String LEGACY_TMP_REGEX_STR = "^((.*)\\-(.*)\\-)?tmp(link)?\\-((?:l|k).)\\-(\\d)*\\-(.*)$"; | ||
188 | + private final static Pattern LEGACY_TMP_REGEX = Pattern.compile(LEGACY_TMP_REGEX_STR); | ||
189 | + | ||
190 | + public static boolean isLegacyFile(File file) | ||
191 | + { | ||
192 | + if (file.isDirectory()) | ||
193 | + return file.getParentFile() != null && | ||
194 | + file.getParentFile().getName().equalsIgnoreCase("system") && | ||
195 | + LEGACY_COMP_IN_PROG_REGEX.matcher(file.getName()).matches(); | ||
196 | + else | ||
197 | + return LEGACY_TMP_REGEX.matcher(file.getName()).matches(); | ||
198 | + } | ||
199 | + | ||
200 | + public static boolean isValidFile(String fileName) | ||
201 | + { | ||
202 | + return fileName.endsWith(".db") && !LEGACY_TMP_REGEX.matcher(fileName).matches(); | ||
203 | + } | ||
204 | + | ||
205 | + /** | ||
206 | + * @see #fromFilename(File directory, String name) | ||
207 | + * @param filename The SSTable filename | ||
208 | + * @return Descriptor of the SSTable initialized from filename | ||
209 | + */ | ||
210 | + public static Descriptor fromFilename(String filename) | ||
211 | + { | ||
212 | + return fromFilename(filename, false); | ||
213 | + } | ||
214 | + | ||
215 | + public static Descriptor fromFilename(String filename, SSTableFormat.Type formatType) | ||
216 | + { | ||
217 | + return fromFilename(filename).withFormatType(formatType); | ||
218 | + } | ||
219 | + | ||
220 | + public static Descriptor fromFilename(String filename, boolean skipComponent) | ||
221 | + { | ||
222 | + File file = new File(filename).getAbsoluteFile(); | ||
223 | + return fromFilename(file.getParentFile(), file.getName(), skipComponent).left; | ||
224 | + } | ||
225 | + | ||
226 | + public static Pair<Descriptor, String> fromFilename(File directory, String name) | ||
227 | + { | ||
228 | + return fromFilename(directory, name, false); | ||
229 | + } | ||
230 | + | ||
231 | + /** | ||
232 | + * Filename of the form is vary by version: | ||
233 | + * | ||
234 | + * <ul> | ||
235 | + * <li><ksname>-<cfname>-(tmp-)?<version>-<gen>-<component> for cassandra 2.0 and before</li> | ||
236 | + * <li>(<tmp marker>-)?<version>-<gen>-<component> for cassandra 3.0 and later</li> | ||
237 | + * </ul> | ||
238 | + * | ||
239 | + * If this is for SSTable of secondary index, directory should ends with index name for 2.1+. | ||
240 | + * | ||
241 | + * @param directory The directory of the SSTable files | ||
242 | + * @param name The name of the SSTable file | ||
243 | + * @param skipComponent true if the name param should not be parsed for a component tag | ||
244 | + * | ||
245 | + * @return A Descriptor for the SSTable, and the Component remainder. | ||
246 | + */ | ||
247 | + public static Pair<Descriptor, String> fromFilename(File directory, String name, boolean skipComponent) | ||
248 | + { | ||
249 | + File parentDirectory = directory != null ? directory : new File("."); | ||
250 | + | ||
251 | + // tokenize the filename | ||
252 | + StringTokenizer st = new StringTokenizer(name, String.valueOf(separator)); | ||
253 | + String nexttok; | ||
254 | + | ||
255 | + // read tokens backwards to determine version | ||
256 | + Deque<String> tokenStack = new ArrayDeque<>(); | ||
257 | + while (st.hasMoreTokens()) | ||
258 | + { | ||
259 | + tokenStack.push(st.nextToken()); | ||
260 | + } | ||
261 | + | ||
262 | + // component suffix | ||
263 | + String component = skipComponent ? null : tokenStack.pop(); | ||
264 | + | ||
265 | + nexttok = tokenStack.pop(); | ||
266 | + // generation OR format type | ||
267 | + SSTableFormat.Type fmt = SSTableFormat.Type.LEGACY; | ||
268 | + if (!CharMatcher.digit().matchesAllOf(nexttok)) | ||
269 | + { | ||
270 | + fmt = SSTableFormat.Type.validate(nexttok); | ||
271 | + nexttok = tokenStack.pop(); | ||
272 | + } | ||
273 | + | ||
274 | + // generation | ||
275 | + int generation = Integer.parseInt(nexttok); | ||
276 | + | ||
277 | + // version | ||
278 | + nexttok = tokenStack.pop(); | ||
279 | + | ||
280 | + if (!Version.validate(nexttok)) | ||
281 | + throw new UnsupportedOperationException("SSTable " + name + " is too old to open. Upgrade to 2.0 first, and run upgradesstables"); | ||
282 | + | ||
283 | + Version version = fmt.info.getVersion(nexttok); | ||
284 | + | ||
285 | + // ks/cf names | ||
286 | + String ksname, cfname; | ||
287 | + if (version.hasNewFileName()) | ||
288 | + { | ||
289 | + // for 2.1+ read ks and cf names from directory | ||
290 | + File cfDirectory = parentDirectory; | ||
291 | + // check if this is secondary index | ||
292 | + String indexName = ""; | ||
293 | + if (cfDirectory.getName().startsWith(Directories.SECONDARY_INDEX_NAME_SEPARATOR)) | ||
294 | + { | ||
295 | + indexName = cfDirectory.getName(); | ||
296 | + cfDirectory = cfDirectory.getParentFile(); | ||
297 | + } | ||
298 | + if (cfDirectory.getName().equals(Directories.BACKUPS_SUBDIR)) | ||
299 | + { | ||
300 | + cfDirectory = cfDirectory.getParentFile(); | ||
301 | + } | ||
302 | + else if (cfDirectory.getParentFile().getName().equals(Directories.SNAPSHOT_SUBDIR)) | ||
303 | + { | ||
304 | + cfDirectory = cfDirectory.getParentFile().getParentFile(); | ||
305 | + } | ||
306 | + cfname = cfDirectory.getName().split("-")[0] + indexName; | ||
307 | + ksname = cfDirectory.getParentFile().getName(); | ||
308 | + } | ||
309 | + else | ||
310 | + { | ||
311 | + cfname = tokenStack.pop(); | ||
312 | + ksname = tokenStack.pop(); | ||
313 | + } | ||
314 | + assert tokenStack.isEmpty() : "Invalid file name " + name + " in " + directory; | ||
315 | + | ||
316 | + return Pair.create(new Descriptor(version, parentDirectory, ksname, cfname, generation, fmt, | ||
317 | + // _assume_ version from version | ||
318 | + Component.digestFor(version.uncompressedChecksumType())), | ||
319 | + component); | ||
320 | + } | ||
321 | + | ||
322 | + public IMetadataSerializer getMetadataSerializer() | ||
323 | + { | ||
324 | + if (version.hasNewStatsFile()) | ||
325 | + return new MetadataSerializer(); | ||
326 | + else | ||
327 | + return new LegacyMetadataSerializer(); | ||
328 | + } | ||
329 | + | ||
330 | + /** | ||
331 | + * @return true if the current Cassandra version can read the given sstable version | ||
332 | + */ | ||
333 | + public boolean isCompatible() | ||
334 | + { | ||
335 | + return version.isCompatible(); | ||
336 | + } | ||
337 | + | ||
338 | + @Override | ||
339 | + public String toString() | ||
340 | + { | ||
341 | + return baseFilename(); | ||
342 | + } | ||
343 | + | ||
344 | + @Override | ||
345 | + public boolean equals(Object o) | ||
346 | + { | ||
347 | + if (o == this) | ||
348 | + return true; | ||
349 | + if (!(o instanceof Descriptor)) | ||
350 | + return false; | ||
351 | + Descriptor that = (Descriptor)o; | ||
352 | + return that.directory.equals(this.directory) | ||
353 | + && that.generation == this.generation | ||
354 | + && that.ksname.equals(this.ksname) | ||
355 | + && that.cfname.equals(this.cfname) | ||
356 | + && that.formatType == this.formatType; | ||
357 | + } | ||
358 | + | ||
359 | + @Override | ||
360 | + public int hashCode() | ||
361 | + { | ||
362 | + return hashCode; | ||
363 | + } | ||
364 | +} |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, software | ||
13 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
14 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
15 | + * See the License for the specific language governing permissions and | ||
16 | + * limitations under the License. | ||
17 | + */ | ||
18 | +package org.apache.cassandra.io.sstable.format; | ||
19 | + | ||
20 | +import com.google.common.base.CharMatcher; | ||
21 | +import org.apache.cassandra.config.CFMetaData; | ||
22 | +import org.apache.cassandra.db.RowIndexEntry; | ||
23 | +import org.apache.cassandra.db.SerializationHeader; | ||
24 | +import org.apache.cassandra.io.sstable.format.big.BigFormat; | ||
25 | + | ||
26 | +/** | ||
27 | + * Provides the accessors to data on disk. | ||
28 | + */ | ||
29 | +public interface SSTableFormat | ||
30 | +{ | ||
31 | + static boolean enableSSTableDevelopmentTestMode = Boolean.getBoolean("cassandra.test.sstableformatdevelopment"); | ||
32 | + | ||
33 | + | ||
34 | + Version getLatestVersion(); | ||
35 | + Version getVersion(String version); | ||
36 | + | ||
37 | + SSTableWriter.Factory getWriterFactory(); | ||
38 | + SSTableReader.Factory getReaderFactory(); | ||
39 | + | ||
40 | + RowIndexEntry.IndexSerializer<?> getIndexSerializer(CFMetaData cfm, Version version, SerializationHeader header); | ||
41 | + | ||
42 | + public static enum Type | ||
43 | + { | ||
44 | + //Used internally to refer to files with no | ||
45 | + //format flag in the filename | ||
46 | + LEGACY("big", BigFormat.instance), | ||
47 | + | ||
48 | + //The original sstable format | ||
49 | + BIG("big", BigFormat.instance); | ||
50 | + | ||
51 | + public final SSTableFormat info; | ||
52 | + public final String name; | ||
53 | + | ||
54 | + public static Type current() | ||
55 | + { | ||
56 | + return BIG; | ||
57 | + } | ||
58 | + | ||
59 | + private Type(String name, SSTableFormat info) | ||
60 | + { | ||
61 | + //Since format comes right after generation | ||
62 | + //we disallow formats with numeric names | ||
63 | + // We have removed this check for compatibility with the embedded cassandra used for tests. | ||
64 | + assert !CharMatcher.digit().matchesAllOf(name); | ||
65 | + | ||
66 | + this.name = name; | ||
67 | + this.info = info; | ||
68 | + } | ||
69 | + | ||
70 | + public static Type validate(String name) | ||
71 | + { | ||
72 | + for (Type valid : Type.values()) | ||
73 | + { | ||
74 | + //This is used internally for old sstables | ||
75 | + if (valid == LEGACY) | ||
76 | + continue; | ||
77 | + | ||
78 | + if (valid.name.equalsIgnoreCase(name)) | ||
79 | + return valid; | ||
80 | + } | ||
81 | + | ||
82 | + throw new IllegalArgumentException("No Type constant " + name); | ||
83 | + } | ||
84 | + } | ||
85 | +} |
@@ -728,6 +728,7 @@ | @@ -728,6 +728,7 @@ | ||
728 | <exclude>ui/**</exclude> | 728 | <exclude>ui/**</exclude> |
729 | <exclude>src/browserslist</exclude> | 729 | <exclude>src/browserslist</exclude> |
730 | <exclude>**/*.raw</exclude> | 730 | <exclude>**/*.raw</exclude> |
731 | + <exclude>**/apache/cassandra/io/**</exclude> | ||
731 | </excludes> | 732 | </excludes> |
732 | <mapping> | 733 | <mapping> |
733 | <proto>JAVADOC_STYLE</proto> | 734 | <proto>JAVADOC_STYLE</proto> |
@@ -158,16 +158,21 @@ public class AlarmRuleState { | @@ -158,16 +158,21 @@ public class AlarmRuleState { | ||
158 | return false; | 158 | return false; |
159 | } | 159 | } |
160 | 160 | ||
161 | + public void clear() { | ||
162 | + if (state.getEventCount() > 0 || state.getLastEventTs() > 0 || state.getDuration() > 0) { | ||
163 | + state.setEventCount(0L); | ||
164 | + state.setLastEventTs(0L); | ||
165 | + state.setDuration(0L); | ||
166 | + updateFlag = true; | ||
167 | + } | ||
168 | + } | ||
169 | + | ||
161 | private boolean evalRepeating(DeviceDataSnapshot data, boolean active) { | 170 | private boolean evalRepeating(DeviceDataSnapshot data, boolean active) { |
162 | if (active && eval(alarmRule.getCondition(), data)) { | 171 | if (active && eval(alarmRule.getCondition(), data)) { |
163 | state.setEventCount(state.getEventCount() + 1); | 172 | state.setEventCount(state.getEventCount() + 1); |
164 | updateFlag = true; | 173 | updateFlag = true; |
165 | - return state.getEventCount() > requiredRepeats; | 174 | + return state.getEventCount() >= requiredRepeats; |
166 | } else { | 175 | } else { |
167 | - if (state.getEventCount() > 0) { | ||
168 | - state.setEventCount(0L); | ||
169 | - updateFlag = true; | ||
170 | - } | ||
171 | return false; | 176 | return false; |
172 | } | 177 | } |
173 | } | 178 | } |
@@ -187,11 +192,6 @@ public class AlarmRuleState { | @@ -187,11 +192,6 @@ public class AlarmRuleState { | ||
187 | } | 192 | } |
188 | return state.getDuration() > requiredDurationInMs; | 193 | return state.getDuration() > requiredDurationInMs; |
189 | } else { | 194 | } else { |
190 | - if (state.getLastEventTs() > 0 || state.getDuration() > 0) { | ||
191 | - state.setLastEventTs(0L); | ||
192 | - state.setDuration(0L); | ||
193 | - updateFlag = true; | ||
194 | - } | ||
195 | return false; | 195 | return false; |
196 | } | 196 | } |
197 | } | 197 | } |
@@ -204,13 +204,7 @@ public class AlarmRuleState { | @@ -204,13 +204,7 @@ public class AlarmRuleState { | ||
204 | case DURATION: | 204 | case DURATION: |
205 | if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { | 205 | if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { |
206 | long duration = state.getDuration() + (ts - state.getLastEventTs()); | 206 | long duration = state.getDuration() + (ts - state.getLastEventTs()); |
207 | - boolean result = duration > requiredDurationInMs && isActive(ts); | ||
208 | - if (result) { | ||
209 | - state.setLastEventTs(0L); | ||
210 | - state.setDuration(0L); | ||
211 | - updateFlag = true; | ||
212 | - } | ||
213 | - return result; | 207 | + return duration > requiredDurationInMs && isActive(ts); |
214 | } | 208 | } |
215 | default: | 209 | default: |
216 | return false; | 210 | return false; |
@@ -53,7 +53,6 @@ class DeviceProfileAlarmState { | @@ -53,7 +53,6 @@ class DeviceProfileAlarmState { | ||
53 | public DeviceProfileAlarmState(EntityId originator, DeviceProfileAlarm alarmDefinition, PersistedAlarmState alarmState) { | 53 | public DeviceProfileAlarmState(EntityId originator, DeviceProfileAlarm alarmDefinition, PersistedAlarmState alarmState) { |
54 | this.originator = originator; | 54 | this.originator = originator; |
55 | this.updateState(alarmDefinition, alarmState); | 55 | this.updateState(alarmDefinition, alarmState); |
56 | - | ||
57 | } | 56 | } |
58 | 57 | ||
59 | public boolean process(TbContext ctx, TbMsg msg, DeviceDataSnapshot data) throws ExecutionException, InterruptedException { | 58 | public boolean process(TbContext ctx, TbMsg msg, DeviceDataSnapshot data) throws ExecutionException, InterruptedException { |
@@ -179,5 +178,15 @@ class DeviceProfileAlarmState { | @@ -179,5 +178,15 @@ class DeviceProfileAlarmState { | ||
179 | } | 178 | } |
180 | } | 179 | } |
181 | 180 | ||
182 | - | 181 | + public boolean processAlarmClear(TbContext ctx, Alarm alarmNf) { |
182 | + boolean updated = false; | ||
183 | + if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { | ||
184 | + currentAlarm = null; | ||
185 | + for (AlarmRuleState state : createRulesSortedBySeverityDesc) { | ||
186 | + state.clear(); | ||
187 | + updated |= state.checkUpdate(); | ||
188 | + } | ||
189 | + } | ||
190 | + return updated; | ||
191 | + } | ||
183 | } | 192 | } |
@@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode; | @@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode; | ||
24 | import org.thingsboard.server.common.data.DataConstants; | 24 | import org.thingsboard.server.common.data.DataConstants; |
25 | import org.thingsboard.server.common.data.Device; | 25 | import org.thingsboard.server.common.data.Device; |
26 | import org.thingsboard.server.common.data.DeviceProfile; | 26 | import org.thingsboard.server.common.data.DeviceProfile; |
27 | +import org.thingsboard.server.common.data.alarm.Alarm; | ||
27 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | 28 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
28 | import org.thingsboard.server.common.data.id.DeviceId; | 29 | import org.thingsboard.server.common.data.id.DeviceId; |
29 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 30 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
@@ -130,6 +131,8 @@ class DeviceState { | @@ -130,6 +131,8 @@ class DeviceState { | ||
130 | stateChanged = processAttributesUpdateNotification(ctx, msg); | 131 | stateChanged = processAttributesUpdateNotification(ctx, msg); |
131 | } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { | 132 | } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { |
132 | stateChanged = processAttributesDeleteNotification(ctx, msg); | 133 | stateChanged = processAttributesDeleteNotification(ctx, msg); |
134 | + } else if (msg.getType().equals(DataConstants.ALARM_CLEAR)) { | ||
135 | + stateChanged = processAlarmClearNotification(ctx, msg); | ||
133 | } else { | 136 | } else { |
134 | ctx.tellSuccess(msg); | 137 | ctx.tellSuccess(msg); |
135 | } | 138 | } |
@@ -139,6 +142,18 @@ class DeviceState { | @@ -139,6 +142,18 @@ class DeviceState { | ||
139 | } | 142 | } |
140 | } | 143 | } |
141 | 144 | ||
145 | + private boolean processAlarmClearNotification(TbContext ctx, TbMsg msg) { | ||
146 | + boolean stateChanged = false; | ||
147 | + Alarm alarmNf = JacksonUtil.fromString(msg.getData(), Alarm.class); | ||
148 | + for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { | ||
149 | + DeviceProfileAlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), | ||
150 | + a -> new DeviceProfileAlarmState(deviceId, alarm, getOrInitPersistedAlarmState(alarm))); | ||
151 | + stateChanged |= alarmState.processAlarmClear(ctx, alarmNf); | ||
152 | + } | ||
153 | + ctx.tellSuccess(msg); | ||
154 | + return stateChanged; | ||
155 | + } | ||
156 | + | ||
142 | private boolean processAttributesUpdateNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { | 157 | private boolean processAttributesUpdateNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { |
143 | Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())); | 158 | Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())); |
144 | String scope = msg.getMetaData().getValue("scope"); | 159 | String scope = msg.getMetaData().getValue("scope"); |
@@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; | @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; | ||
25 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | 25 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
26 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 26 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
27 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 27 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
28 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
28 | 29 | ||
29 | import javax.net.ssl.*; | 30 | import javax.net.ssl.*; |
30 | import java.io.File; | 31 | import java.io.File; |
@@ -71,7 +72,7 @@ public class MqttSslClient { | @@ -71,7 +72,7 @@ public class MqttSslClient { | ||
71 | 72 | ||
72 | MqttConnectOptions options = new MqttConnectOptions(); | 73 | MqttConnectOptions options = new MqttConnectOptions(); |
73 | options.setSocketFactory(sslContext.getSocketFactory()); | 74 | options.setSocketFactory(sslContext.getSocketFactory()); |
74 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, CLIENT_ID); | 75 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, CLIENT_ID, new MemoryPersistence()); |
75 | client.connect(options); | 76 | client.connect(options); |
76 | Thread.sleep(3000); | 77 | Thread.sleep(3000); |
77 | MqttMessage message = new MqttMessage(); | 78 | MqttMessage message = new MqttMessage(); |