View Javadoc

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   * Encapsulates information that is passed to {@link MessageRunnerService#addMessage(Message)}.
13   *
14   * <p>A {@code Message} contains a {@link MessageRunnerKey} which specifies a {@link com.atlassian.messagequeue.registry.MessageRunner}
15   * with which the {@code Message} should be handled by.
16   *
17   * <p>A {@code Message} also contains an optional String payload that is passed to the execution of the corresponding
18   * {@link com.atlassian.messagequeue.registry.MessageRunner}.
19   *
20   * @since 1.0
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       * Returns the {@link MessageRunnerKey} which specifies a {@link com.atlassian.messagequeue.registry.MessageRunner}
42       * with which this <tt>Message</tt> should be handled by.
43       *
44       * @return message runner key
45       */
46      public MessageRunnerKey getRunnerKey() {
47          return runnerKey;
48      }
49  
50      /**
51       * @return Returns the message payload or {@link Optional#empty()} when no payload was specified in the construction of a <tt>Message</tt>
52       * (or null was specified for construction).
53       */
54      public Optional<String> getPayload() {
55          return Optional.ofNullable(payload);
56      }
57  
58      /**
59       * Message factory method
60       *
61       * @param runnerKey message runner key
62       * @param payload message payload
63       *
64       * @return Message instance
65       * @throws MessagePayloadSizeExceededException if payload exceeds {@link MessageRunnerConstants#payloadMaxSize()}.
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       * For valid code point definitions also see https://www.w3.org/TR/REC-xml/#charsets
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      * Builder factory method
119      * @param runnerKey message runner key
120      * @return Message Builder
121      */
122     public static Builder builder(MessageRunnerKey runnerKey) {
123         return new Builder(runnerKey);
124     }
125 
126     /**
127      * Message Builder
128      */
129     @PublicApi
130     public static class Builder {
131         private final MessageRunnerKey runnerKey;
132         @Nullable private String payload;
133 
134         /**
135          * @param runnerKey message runner key
136          */
137         Builder(MessageRunnerKey runnerKey) {
138             this.runnerKey = requireNonNull(runnerKey, "runnerKey");
139         }
140 
141         /**
142          * @return a message instance
143          */
144         public Message build() {
145             return new Message(runnerKey, payload);
146         }
147 
148         /**
149          * @param payload message payload
150          * @return message
151          */
152         public Builder payload(@Nullable String payload) {
153             this.payload = payload;
154             return this;
155         }
156     }
157 }