1 package com.atlassian.messagequeue;
2
3 import com.atlassian.annotations.PublicApi;
4
5 import javax.annotation.Nullable;
6 import java.util.Objects;
7 import java.util.Optional;
8
9 import static java.util.Objects.requireNonNull;
10
11
12
13
14
15
16
17
18
19
20
21
22 @PublicApi
23 public final class Message {
24 private final MessageRunnerKey runnerKey;
25 @Nullable private final String payload;
26
27 Message(MessageRunnerKey runnerKey, @Nullable String payload) {
28 this.runnerKey = requireNonNull(runnerKey, "runnerKey");
29 this.payload = payload;
30
31 if (this.payload != null && this.payload.length() > MessageRunnerConstants.payloadMaxSize()) {
32 throw new MessagePayloadSizeExceededException(runnerKey, MessageRunnerConstants.payloadMaxSize());
33 }
34
35 getFirstInvalidCodePoint(payload).ifPresent(codePoint -> {
36 throw new InvalidMessagePayloadException(runnerKey, codePoint);
37 });
38 }
39
40
41
42
43
44
45
46 public MessageRunnerKey getRunnerKey() {
47 return runnerKey;
48 }
49
50
51
52
53
54 public Optional<String> getPayload() {
55 return Optional.ofNullable(payload);
56 }
57
58
59
60
61
62
63
64
65
66
67 public static Message create(MessageRunnerKey runnerKey, @Nullable String payload) {
68 return new Message(runnerKey, payload);
69 }
70
71 private static Optional<Integer> getFirstInvalidCodePoint(@Nullable String payload) {
72 if (payload == null) {
73 return Optional.empty();
74 }
75
76 int codePoint;
77 for (int i = 0; i < payload.length(); i++) {
78 codePoint = Character.codePointAt(payload, i);
79 if (!isValidCodePoint(codePoint)) {
80 return Optional.of(codePoint);
81 }
82 }
83
84 return Optional.empty();
85 }
86
87
88
89
90 private static boolean isValidCodePoint(int codePoint) {
91 return codePoint == 0x9 || codePoint == 0xA || codePoint == 0xD
92 || (0x20 <= codePoint && codePoint <= 0xD7FF)
93 || (0xE000 <= codePoint && codePoint <= 0xFFFD)
94 || (0x10000 <= codePoint && codePoint <= 0x10FFFF);
95 }
96
97 @Override
98 public boolean equals(Object o) {
99 if (this == o) {
100 return true;
101 }
102 if (o == null || getClass() != o.getClass()) {
103 return false;
104 }
105
106 final Message message = (Message) o;
107
108 return Objects.equals(runnerKey, message.runnerKey) &&
109 Objects.equals(payload, message.payload);
110 }
111
112 @Override
113 public int hashCode() {
114 return Objects.hash(runnerKey, payload);
115 }
116
117
118
119
120
121
122 public static Builder builder(MessageRunnerKey runnerKey) {
123 return new Builder(runnerKey);
124 }
125
126
127
128
129 @PublicApi
130 public static class Builder {
131 private final MessageRunnerKey runnerKey;
132 @Nullable private String payload;
133
134
135
136
137 Builder(MessageRunnerKey runnerKey) {
138 this.runnerKey = requireNonNull(runnerKey, "runnerKey");
139 }
140
141
142
143
144 public Message build() {
145 return new Message(runnerKey, payload);
146 }
147
148
149
150
151
152 public Builder payload(@Nullable String payload) {
153 this.payload = payload;
154 return this;
155 }
156 }
157 }