View Javadoc
1   package net.avcompris.commons3.notifier.utils;
2   
3   import static com.google.common.base.Preconditions.checkArgument;
4   import static com.google.common.base.Preconditions.checkNotNull;
5   import static net.avcompris.commons3.databeans.DataBeans.instantiate;
6   import static org.apache.commons.lang3.CharEncoding.UTF_8;
7   import static org.joda.time.DateTimeZone.UTC;
8   
9   import java.io.UnsupportedEncodingException;
10  // import static org.joda.time.format.DateTimeFormatter;
11  //import static java.time.format.DateTimeFormatter.
12  
13  import javax.annotation.Nullable;
14  
15  import org.joda.time.DateTime;
16  import org.joda.time.format.DateTimeFormatter;
17  import org.joda.time.format.ISODateTimeFormat;
18  import org.json.simple.JSONArray;
19  import org.json.simple.JSONObject;
20  import org.json.simple.parser.JSONParser;
21  import org.json.simple.parser.ParseException;
22  
23  import net.avcompris.commons3.notifier.api.ErrorNotification;
24  import net.avcompris.commons3.notifier.api.Notification;
25  import net.avcompris.commons3.notifier.api.Notification.ActionType;
26  
27  public abstract class NotificationUtils {
28  
29  	private static final DateTimeFormatter ISO_FORMATTER = ISODateTimeFormat.dateTime();
30  
31  	public static byte[] serialize(final Notification notification) {
32  
33  		final String json = serializeToJSON(notification);
34  
35  		try {
36  
37  			return json.getBytes(UTF_8);
38  
39  		} catch (final UnsupportedEncodingException e) {
40  
41  			throw new RuntimeException(e);
42  		}
43  	}
44  
45  	private static String serializeToJSON(final Notification notification) {
46  
47  		checkNotNull(notification, "notification");
48  
49  		final ActionType actionType = notification.getActionType();
50  		final String username = notification.getUsername();
51  
52  		final StringBuilder sb = new StringBuilder("{");
53  
54  		sb.append("\"actionType\": ");
55  
56  		sb.append("\"").append(actionType.name()).append("\"");
57  
58  		sb.append(", \"username\": ");
59  
60  		if (username == null) {
61  
62  			sb.append("null");
63  
64  		} else {
65  
66  			sb.append("\"").append(username).append("\"");
67  		}
68  
69  		return sb.append("}").toString();
70  	}
71  
72  	public static Notification deserialize( //
73  			final Class<? extends ActionType> actionTypeClass, //
74  			final byte[] bytes) {
75  
76  		checkNotNull(bytes, "bytes");
77  		checkNotNull(actionTypeClass, "actionTypeClass");
78  
79  		final String s;
80  
81  		try {
82  
83  			s = new String(bytes, UTF_8);
84  
85  		} catch (final UnsupportedEncodingException e) {
86  
87  			throw new RuntimeException(e);
88  		}
89  
90  		final JSONObject json;
91  
92  		try {
93  
94  			json = (JSONObject) new JSONParser().parse(s);
95  
96  		} catch (final ParseException e) {
97  
98  			throw new RuntimeException("JSON: " + s, e);
99  		}
100 
101 		final Object actionTypeAsString = json.get("actionType");
102 		final Object username = json.get("username");
103 
104 		if (actionTypeAsString == null) {
105 			throw new RuntimeException("actionType should not be null in JSON: " + s);
106 		}
107 
108 		final ActionType actionType = extractActionType(actionTypeClass, actionTypeAsString.toString());
109 
110 		final Notification notification = instantiate(Notification.class) //
111 				.setActionType(actionType);
112 
113 		if (username != null) {
114 
115 			notification.setUsername(username.toString());
116 		}
117 
118 		return notification;
119 	}
120 
121 	private static ActionType extractActionType(final Class<? extends ActionType> actionTypeClass, final String name) {
122 
123 		checkNotNull(actionTypeClass, "actionTypeClass");
124 		checkNotNull(name, "name");
125 
126 		checkArgument(actionTypeClass.isEnum(), //
127 				"actionTypeClass should be Enum: %s", actionTypeClass.getName());
128 
129 		for (final ActionType actionType : actionTypeClass.getEnumConstants()) {
130 
131 			if (name.contentEquals(actionType.name())) {
132 
133 				return actionType;
134 			}
135 		}
136 
137 		throw new RuntimeException("actionType in JSON cannot be parsed: " + name);
138 	}
139 
140 	public static byte[] serialize(final Throwable throwable) {
141 
142 		final String json = serializeToJSON(throwable);
143 
144 		try {
145 
146 			return json.getBytes(UTF_8);
147 
148 		} catch (final UnsupportedEncodingException e) {
149 
150 			throw new RuntimeException(e);
151 		}
152 	}
153 
154 	private static String escape(final String s) {
155 
156 		checkNotNull(s, "s");
157 
158 		return s.replace("\\", "\\\\").replace("\"", "\\\"");
159 	}
160 
161 	private static String serializeToJSON(final Throwable throwable) {
162 
163 		checkNotNull(throwable, "throwable");
164 
165 		final DateTime dateTime = new DateTime().withZone(UTC);
166 
167 		final Class<? extends Throwable> clazz = throwable.getClass();
168 
169 		@Nullable
170 		final String message = throwable.getMessage();
171 
172 		@Nullable
173 		final Throwable cause = throwable.getCause();
174 
175 		final StringBuilder sb = new StringBuilder("{");
176 
177 		sb.append("\"dateTime\": ");
178 
179 		sb.append("\"").append(dateTime.toString(ISO_FORMATTER)).append("\"");
180 
181 		sb.append(", \"className\": ");
182 
183 		sb.append("\"").append(clazz.getName()).append("\"");
184 
185 		sb.append(", \"message\": ");
186 
187 		appendQuotedStringOrNull(sb, message);
188 
189 		sb.append(", \"cause\": ");
190 
191 		if (cause == null) {
192 
193 			sb.append("null");
194 
195 		} else {
196 
197 			sb.append(serializeToJSON(cause));
198 		}
199 
200 		sb.append(", \"stackTrace\": [");
201 
202 		boolean start = true;
203 
204 		for (final StackTraceElement ste : throwable.getStackTrace()) {
205 
206 			if (!start) {
207 				sb.append(", ");
208 			} else {
209 				start = false;
210 			}
211 
212 			sb.append("{");
213 
214 			sb.append("\"className\": ");
215 
216 			appendQuotedStringOrNull(sb, ste.getClassName());
217 
218 			sb.append(", \"methodName\": ");
219 
220 			appendQuotedStringOrNull(sb, ste.getMethodName());
221 
222 			sb.append(", \"fileName\": ");
223 
224 			appendQuotedStringOrNull(sb, ste.getFileName());
225 
226 			sb.append(", \"lineNumber\": ");
227 
228 			sb.append(ste.getLineNumber());
229 
230 			sb.append("}");
231 		}
232 
233 		sb.append("]");
234 
235 		return sb.append("}").toString();
236 	}
237 
238 	private static void appendQuotedStringOrNull(final StringBuilder sb, @Nullable final String s) {
239 
240 		if (s == null) {
241 
242 			sb.append("null");
243 
244 		} else {
245 
246 			sb.append("\"").append(escape(s)).append("\"");
247 		}
248 	}
249 
250 	public static ErrorNotification deserializeError(final byte[] bytes) {
251 
252 		checkNotNull(bytes, "bytes");
253 
254 		final String s;
255 
256 		try {
257 
258 			s = new String(bytes, UTF_8);
259 
260 		} catch (final UnsupportedEncodingException e) {
261 
262 			throw new RuntimeException(e);
263 		}
264 
265 		final JSONObject json;
266 
267 		try {
268 
269 			json = (JSONObject) new JSONParser().parse(s);
270 
271 		} catch (final ParseException e) {
272 
273 			throw new RuntimeException("JSON: " + s, e);
274 		}
275 
276 		return deserializeError(json);
277 	}
278 
279 	private static ErrorNotification deserializeError(final JSONObject json) {
280 
281 		final DateTime dateTime = ISO_FORMATTER.parseDateTime(json.get("dateTime").toString());
282 
283 		final String className = json.get("className").toString();
284 
285 		@Nullable
286 		final Object cause = json.get("cause");
287 
288 		final JSONArray stackTrace = (JSONArray) json.get("stackTrace");
289 
290 		final ErrorNotification notification = instantiate(ErrorNotification.class) //
291 				.setDateTime(dateTime) //
292 				.setClassName(className) //
293 				.setMessage(toStringOrNull(json.get("message")));
294 
295 		if (cause != null) {
296 
297 			notification.setCause(deserializeError((JSONObject) cause));
298 		}
299 
300 		for (final Object item : stackTrace) {
301 
302 			final JSONObject ste = (JSONObject) item;
303 
304 			notification.addToStackTrace(instantiate(ErrorNotification.StackTraceElement.class) //
305 					.setClassName(toStringOrNull(ste.get("className"))) //
306 					.setMethodName(toStringOrNull(ste.get("methodName"))) //
307 					.setFileName(toStringOrNull(ste.get("fileName"))) //
308 					.setLineNumber((int) (long) ste.get("lineNumber")));
309 		}
310 
311 		return notification;
312 	}
313 
314 	@Nullable
315 	private static String toStringOrNull(@Nullable final Object o) {
316 
317 		return o == null ? null : o.toString();
318 	}
319 
320 	public static ErrorNotification toErrorNotification(final Throwable throwable) {
321 
322 		checkNotNull(throwable, "throwable");
323 
324 		final DateTime dateTime = new DateTime().withZone(UTC);
325 
326 		final String className = throwable.getClass().getName();
327 
328 		@Nullable
329 		final String message = throwable.getMessage();
330 
331 		@Nullable
332 		final Throwable cause = throwable.getCause();
333 
334 		final ErrorNotification notification = instantiate(ErrorNotification.class) //
335 				.setDateTime(dateTime) //
336 				.setClassName(className) //
337 				.setMessage(message);
338 
339 		if (cause != null) {
340 
341 			notification.setCause(toErrorNotification(cause));
342 		}
343 
344 		for (final StackTraceElement ste : throwable.getStackTrace()) {
345 
346 			notification.addToStackTrace(instantiate(ErrorNotification.StackTraceElement.class) //
347 					.setClassName(ste.getClassName()) //
348 					.setMethodName(ste.getMethodName()) //
349 					.setFileName(ste.getFileName()) //
350 					.setLineNumber(ste.getLineNumber()));
351 		}
352 
353 		return notification;
354 	}
355 }