1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.scheduler.remote;
23
24 import static java.nio.charset.StandardCharsets.UTF_8;
25 import static org.apache.http.HttpStatus.SC_BAD_REQUEST;
26 import static org.apache.http.HttpStatus.SC_CONFLICT;
27 import static org.apache.http.HttpStatus.SC_CREATED;
28 import static org.apache.http.HttpStatus.SC_FORBIDDEN;
29 import static org.apache.http.HttpStatus.SC_NOT_FOUND;
30 import static org.apache.http.HttpStatus.SC_NO_CONTENT;
31 import static org.apache.http.HttpStatus.SC_OK;
32 import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
33
34 import org.opencastproject.mediapackage.MediaPackage;
35 import org.opencastproject.mediapackage.MediaPackageParser;
36 import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
37 import org.opencastproject.metadata.dublincore.DublinCores;
38 import org.opencastproject.scheduler.api.Recording;
39 import org.opencastproject.scheduler.api.RecordingImpl;
40 import org.opencastproject.scheduler.api.SchedulerConflictException;
41 import org.opencastproject.scheduler.api.SchedulerException;
42 import org.opencastproject.scheduler.api.SchedulerService;
43 import org.opencastproject.scheduler.api.TechnicalMetadata;
44 import org.opencastproject.scheduler.api.TechnicalMetadataImpl;
45 import org.opencastproject.security.api.TrustedHttpClient;
46 import org.opencastproject.security.api.UnauthorizedException;
47 import org.opencastproject.serviceregistry.api.RemoteBase;
48 import org.opencastproject.serviceregistry.api.ServiceRegistry;
49 import org.opencastproject.util.DateTimeSupport;
50 import org.opencastproject.util.NotFoundException;
51 import org.opencastproject.util.UrlSupport;
52
53 import net.fortuna.ical4j.model.Period;
54 import net.fortuna.ical4j.model.property.RRule;
55
56 import org.apache.commons.lang3.BooleanUtils;
57 import org.apache.commons.lang3.StringUtils;
58 import org.apache.http.HttpResponse;
59 import org.apache.http.NameValuePair;
60 import org.apache.http.client.entity.UrlEncodedFormEntity;
61 import org.apache.http.client.methods.HttpDelete;
62 import org.apache.http.client.methods.HttpGet;
63 import org.apache.http.client.methods.HttpPost;
64 import org.apache.http.client.methods.HttpPut;
65 import org.apache.http.client.utils.URLEncodedUtils;
66 import org.apache.http.message.BasicNameValuePair;
67 import org.apache.http.util.EntityUtils;
68 import org.json.simple.JSONArray;
69 import org.json.simple.JSONObject;
70 import org.json.simple.parser.JSONParser;
71 import org.osgi.service.component.annotations.Component;
72 import org.osgi.service.component.annotations.Reference;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 import java.util.ArrayList;
77 import java.util.Collections;
78 import java.util.Date;
79 import java.util.HashMap;
80 import java.util.HashSet;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Map.Entry;
84 import java.util.Optional;
85 import java.util.Properties;
86 import java.util.Set;
87 import java.util.TimeZone;
88
89
90
91
92 @Component(
93 immediate = true,
94 service = SchedulerService.class,
95 property = {
96 "service.description=Scheduler Remote Service Proxy"
97 }
98 )
99 public class SchedulerServiceRemoteImpl extends RemoteBase implements SchedulerService {
100
101 private static final Logger logger = LoggerFactory.getLogger(SchedulerServiceRemoteImpl.class);
102
103
104 private final JSONParser parser = new JSONParser();
105
106 public SchedulerServiceRemoteImpl() {
107 super(JOB_TYPE);
108 }
109
110 @Override
111 public void addEvent(Date startDateTime, Date endDateTime, String captureAgentId, Set<String> userIds,
112 MediaPackage mediaPackage, Map<String, String> wfProperties, Map<String, String> caMetadata,
113 Optional<String> schedulingSource) throws UnauthorizedException, SchedulerConflictException,
114 SchedulerException {
115 HttpPost post = new HttpPost("/");
116 String eventId = mediaPackage.getIdentifier().toString();
117 logger.debug("Start adding a new event {} through remote Schedule Service", eventId);
118
119 List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
120 params.add(new BasicNameValuePair("start", Long.toString(startDateTime.getTime())));
121 params.add(new BasicNameValuePair("end", Long.toString(endDateTime.getTime())));
122 params.add(new BasicNameValuePair("agent", captureAgentId));
123 params.add(new BasicNameValuePair("users", StringUtils.join(userIds, ",")));
124 params.add(new BasicNameValuePair("mediaPackage", MediaPackageParser.getAsXml(mediaPackage)));
125 params.add(new BasicNameValuePair("wfproperties", toPropertyString(wfProperties)));
126 params.add(new BasicNameValuePair("agentparameters", toPropertyString(caMetadata)));
127 if (schedulingSource.isPresent()) {
128 params.add(new BasicNameValuePair("source", schedulingSource.get()));
129 }
130 post.setEntity(new UrlEncodedFormEntity(params, UTF_8));
131
132 HttpResponse response = getResponse(post, SC_CREATED, SC_UNAUTHORIZED, SC_CONFLICT);
133 try {
134 if (response != null && SC_CREATED == response.getStatusLine().getStatusCode()) {
135 logger.info("Successfully added event {} to the scheduler service", eventId);
136 return;
137 } else if (response != null && SC_CONFLICT == response.getStatusLine().getStatusCode()) {
138 String errorJson = EntityUtils.toString(response.getEntity(), UTF_8);
139 JSONObject json = (JSONObject) parser.parse(errorJson);
140 JSONObject error = (JSONObject) json.get("error");
141 String errorCode = (String) error.get("code");
142 if (SchedulerConflictException.ERROR_CODE.equals(errorCode)) {
143 logger.info("Conflicting events found when adding event {}", eventId);
144 throw new SchedulerConflictException("Conflicting events found when adding event " + eventId);
145 } else {
146 throw new SchedulerException("Unexpected error code " + errorCode);
147 }
148 } else if (response != null && SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
149 logger.info("Unauthorized to create the event");
150 throw new UnauthorizedException("Unauthorized to create the event");
151 } else {
152 throw new SchedulerException("Unable to add event " + eventId + " to the scheduler service");
153 }
154 } catch (UnauthorizedException | SchedulerConflictException e) {
155 throw e;
156 } catch (Exception e) {
157 throw new SchedulerException("Unable to add event " + eventId + " to the scheduler service", e);
158 } finally {
159 closeConnection(response);
160 }
161 }
162
163 @Override
164 public Map<String, Period> addMultipleEvents(RRule rRule, Date start, Date end, Long duration, TimeZone tz,
165 String captureAgentId, Set<String> userIds, MediaPackage templateMp, Map<String, String> wfProperties,
166 Map<String, String> caMetadata, Optional<String> schedulingSource)
167 throws UnauthorizedException, SchedulerConflictException, SchedulerException {
168 HttpPost post = new HttpPost("/");
169 logger.debug("Start adding a new events through remote Schedule Service");
170
171 List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
172 params.add(new BasicNameValuePair("rrule", rRule.getValue()));
173 params.add(new BasicNameValuePair("start", Long.toString(start.getTime())));
174 params.add(new BasicNameValuePair("end", Long.toString(end.getTime())));
175 params.add(new BasicNameValuePair("duration", Long.toString(duration)));
176 params.add(new BasicNameValuePair("tz", tz.toZoneId().getId()));
177 params.add(new BasicNameValuePair("agent", captureAgentId));
178 params.add(new BasicNameValuePair("users", StringUtils.join(userIds, ",")));
179 params.add(new BasicNameValuePair("templateMp", MediaPackageParser.getAsXml(templateMp)));
180 params.add(new BasicNameValuePair("wfproperties", toPropertyString(wfProperties)));
181 params.add(new BasicNameValuePair("agentparameters", toPropertyString(caMetadata)));
182 if (schedulingSource.isPresent()) {
183 params.add(new BasicNameValuePair("source", schedulingSource.get()));
184 }
185 post.setEntity(new UrlEncodedFormEntity(params, UTF_8));
186
187 String eventId = templateMp.getIdentifier().toString();
188
189 HttpResponse response = getResponse(post, SC_CREATED, SC_UNAUTHORIZED, SC_CONFLICT);
190 try {
191 if (response != null && SC_CREATED == response.getStatusLine().getStatusCode()) {
192 logger.info("Successfully added events to the scheduler service");
193 return null;
194 } else if (response != null && SC_CONFLICT == response.getStatusLine().getStatusCode()) {
195 String errorJson = EntityUtils.toString(response.getEntity(), UTF_8);
196 JSONObject json = (JSONObject) parser.parse(errorJson);
197 JSONObject error = (JSONObject) json.get("error");
198 String errorCode = (String) error.get("code");
199 if (SchedulerConflictException.ERROR_CODE.equals(errorCode)) {
200 logger.info("Conflicting events found when adding event based on {}", eventId);
201 throw new SchedulerConflictException("Conflicting events found when adding event based on" + eventId);
202 } else {
203 throw new SchedulerException("Unexpected error code " + errorCode);
204 }
205 } else if (response != null && SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
206 logger.info("Unauthorized to create the event");
207 throw new UnauthorizedException("Unauthorized to create the event");
208 } else {
209 throw new SchedulerException("Unable to add event " + eventId + " to the scheduler service");
210 }
211 } catch (UnauthorizedException | SchedulerConflictException e) {
212 throw e;
213 } catch (Exception e) {
214 throw new SchedulerException("Unable to add event " + eventId + " to the scheduler service", e);
215 } finally {
216 closeConnection(response);
217 }
218 }
219
220 @Override
221 public void updateEvent(String eventId, Optional<Date> startDateTime, Optional<Date> endDateTime,
222 Optional<String> captureAgentId, Optional<Set<String>> userIds, Optional<MediaPackage> mediaPackage,
223 Optional<Map<String, String>> wfProperties, Optional<Map<String, String>> caMetadata)
224 throws NotFoundException, UnauthorizedException, SchedulerConflictException, SchedulerException {
225
226 updateEvent(eventId, startDateTime, endDateTime, captureAgentId, userIds,
227 mediaPackage, wfProperties, caMetadata, false);
228 }
229
230 @Override
231 public void updateEvent(String eventId, Optional<Date> startDateTime, Optional<Date> endDateTime,
232 Optional<String> captureAgentId, Optional<Set<String>> userIds, Optional<MediaPackage> mediaPackage,
233 Optional<Map<String, String>> wfProperties, Optional<Map<String, String>> caMetadata, boolean allowConflict)
234 throws NotFoundException, UnauthorizedException, SchedulerConflictException, SchedulerException {
235
236 logger.debug("Start updating event {}.", eventId);
237 HttpPut put = new HttpPut("/" + eventId);
238
239 List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
240 if (startDateTime.isPresent()) {
241 params.add(new BasicNameValuePair("start", Long.toString(startDateTime.get().getTime())));
242 }
243 if (endDateTime.isPresent()) {
244 params.add(new BasicNameValuePair("end", Long.toString(endDateTime.get().getTime())));
245 }
246 if (captureAgentId.isPresent()) {
247 params.add(new BasicNameValuePair("agent", captureAgentId.get()));
248 }
249 if (userIds.isPresent()) {
250 params.add(new BasicNameValuePair("users", StringUtils.join(userIds.get(), ",")));
251 }
252 if (mediaPackage.isPresent()) {
253 params.add(new BasicNameValuePair("mediaPackage", MediaPackageParser.getAsXml(mediaPackage.get())));
254 }
255 if (wfProperties.isPresent()) {
256 params.add(new BasicNameValuePair("wfproperties", toPropertyString(wfProperties.get())));
257 }
258 if (caMetadata.isPresent()) {
259 params.add(new BasicNameValuePair("agentparameters", toPropertyString(caMetadata.get())));
260 }
261 params.add(new BasicNameValuePair("allowConflict", BooleanUtils.toString(allowConflict, "true", "false", "false")));
262 put.setEntity(new UrlEncodedFormEntity(params, UTF_8));
263
264 HttpResponse response = getResponse(put, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED, SC_FORBIDDEN, SC_CONFLICT);
265 try {
266 if (response != null) {
267 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
268 logger.info("Event {} was not found by the scheduler service", eventId);
269 throw new NotFoundException("Event '" + eventId + "' not found on remote scheduler service!");
270 } else if (SC_OK == response.getStatusLine().getStatusCode()) {
271 logger.info("Event {} successfully updated with capture agent metadata.", eventId);
272 return;
273 } else if (response != null && SC_CONFLICT == response.getStatusLine().getStatusCode()) {
274 String errorJson = EntityUtils.toString(response.getEntity(), UTF_8);
275 JSONObject json = (JSONObject) parser.parse(errorJson);
276 JSONObject error = (JSONObject) json.get("error");
277 String errorCode = (String) error.get("code");
278 if (SchedulerConflictException.ERROR_CODE.equals(errorCode)) {
279 logger.info("Conflicting events found when updating event {}", eventId);
280 throw new SchedulerConflictException("Conflicting events found when updating event " + eventId);
281 } else {
282 throw new SchedulerException("Unexpected error code " + errorCode);
283 }
284 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
285 logger.info("Unauthorized to update the event {}.", eventId);
286 throw new UnauthorizedException("Unauthorized to update the event " + eventId);
287 } else if (SC_FORBIDDEN == response.getStatusLine().getStatusCode()) {
288 logger.info("Forbidden to update the event {}.", eventId);
289 throw new SchedulerException("Event with specified ID cannot be updated");
290 } else {
291 throw new SchedulerException("Unexpected status code " + response.getStatusLine());
292 }
293 }
294 } catch (NotFoundException | SchedulerConflictException | UnauthorizedException e) {
295 throw e;
296 } catch (Exception e) {
297 throw new SchedulerException("Unable to update event " + eventId + " to the scheduler service", e);
298 } finally {
299 closeConnection(response);
300 }
301 throw new SchedulerException("Unable to update event " + eventId);
302 }
303
304 @Override
305 public void removeEvent(String eventId) throws NotFoundException, UnauthorizedException, SchedulerException {
306 logger.debug("Start removing event {} from scheduling service.", eventId);
307 HttpDelete delete = new HttpDelete("/" + eventId);
308
309 HttpResponse response = getResponse(delete, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED, SC_CONFLICT);
310 try {
311 if (response != null && SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
312 logger.info("Event {} was not found by the scheduler service", eventId);
313 throw new NotFoundException("Event '" + eventId + "' not found on remote scheduler service!");
314 } else if (response != null && SC_OK == response.getStatusLine().getStatusCode()) {
315 logger.info("Event {} removed from scheduling service.", eventId);
316 return;
317 } else if (response != null && SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
318 logger.info("Unauthorized to remove the event {}.", eventId);
319 throw new UnauthorizedException("Unauthorized to remove the event " + eventId);
320 }
321 } catch (UnauthorizedException | NotFoundException e) {
322 throw e;
323 } catch (Exception e) {
324 throw new SchedulerException("Unable to remove event " + eventId + " from the scheduler service", e);
325 } finally {
326 closeConnection(response);
327 }
328 throw new SchedulerException("Unable to remove event " + eventId);
329 }
330
331 @Override
332 public MediaPackage getMediaPackage(String eventId)
333 throws NotFoundException, UnauthorizedException, SchedulerException {
334 HttpGet get = new HttpGet(eventId.concat("/mediapackage.xml"));
335 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
336 try {
337 if (response != null) {
338 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
339 throw new NotFoundException("Event mediapackage '" + eventId + "' not found on remote scheduler service!");
340 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
341 logger.info("Unauthorized to get mediapacakge of the event {}.", eventId);
342 throw new UnauthorizedException("Unauthorized to get mediapackage of the event " + eventId);
343 } else {
344 MediaPackage mp = MediaPackageParser.getFromXml(EntityUtils.toString(response.getEntity(), UTF_8));
345 logger.info("Successfully get event mediapackage {} from the remote scheduler service", eventId);
346 return mp;
347 }
348 }
349 } catch (NotFoundException e) {
350 throw e;
351 } catch (UnauthorizedException e) {
352 throw e;
353 } catch (Exception e) {
354 throw new SchedulerException("Unable to parse event media package from remote scheduler service", e);
355 } finally {
356 closeConnection(response);
357 }
358 throw new SchedulerException("Unable to get event media package from remote scheduler service");
359 }
360
361 @Override
362 public DublinCoreCatalog getDublinCore(String eventId)
363 throws NotFoundException, UnauthorizedException, SchedulerException {
364 HttpGet get = new HttpGet(eventId.concat("/dublincore.xml"));
365 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
366 try {
367 if (response != null) {
368 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
369 throw new NotFoundException("Event catalog '" + eventId + "' not found on remote scheduler service!");
370 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
371 logger.info("Unauthorized to get dublincore of the event {}.", eventId);
372 throw new UnauthorizedException("Unauthorized to get dublincore of the event " + eventId);
373 } else {
374 DublinCoreCatalog dublinCoreCatalog = DublinCores.read(response.getEntity().getContent());
375 logger.info("Successfully get event dublincore {} from the remote scheduler service", eventId);
376 return dublinCoreCatalog;
377 }
378 }
379 } catch (NotFoundException | UnauthorizedException e) {
380 throw e;
381 } catch (Exception e) {
382 throw new SchedulerException("Unable to parse event dublincore from remote scheduler service", e);
383 } finally {
384 closeConnection(response);
385 }
386 throw new SchedulerException("Unable to get event dublincore from remote scheduler service");
387 }
388
389 @Override
390 public TechnicalMetadata getTechnicalMetadata(String eventId)
391 throws NotFoundException, UnauthorizedException, SchedulerException {
392 HttpGet get = new HttpGet(eventId.concat("/technical.json"));
393 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
394 try {
395 if (response != null) {
396 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
397 throw new NotFoundException("Event with id '" + eventId + "' not found on remote scheduler service!");
398 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
399 logger.info("Unauthorized to get the technical metadata of the event {}.", eventId);
400 throw new UnauthorizedException("Unauthorized to get the technical metadata of the event " + eventId);
401 } else {
402 String technicalMetadataJson = EntityUtils.toString(response.getEntity(), UTF_8);
403 JSONObject json = (JSONObject) parser.parse(technicalMetadataJson);
404 final String recordingId = (String) json.get("id");
405 final Date start = new Date(DateTimeSupport.fromUTC((String) json.get("start")));
406 final Date end = new Date(DateTimeSupport.fromUTC((String) json.get("end")));
407 final String location = (String) json.get("location");
408
409 final Set<String> presenters = new HashSet<>();
410 JSONArray presentersArr = (JSONArray) json.get("presenters");
411 for (int i = 0; i < presentersArr.size(); i++) {
412 presenters.add((String) presentersArr.get(i));
413 }
414
415 final Map<String, String> wfProperties = new HashMap<>();
416 JSONObject wfPropertiesObj = (JSONObject) json.get("wfProperties");
417 Set<Entry<String, String>> entrySet = wfPropertiesObj.entrySet();
418 for (Entry<String, String> entry : entrySet) {
419 wfProperties.put(entry.getKey(), entry.getValue());
420 }
421
422 final Map<String, String> agentConfig = new HashMap<>();
423 JSONObject agentConfigObj = (JSONObject) json.get("agentConfig");
424 entrySet = agentConfigObj.entrySet();
425 for (Entry<String, String> entry : entrySet) {
426 agentConfig.put(entry.getKey(), entry.getValue());
427 }
428
429 String status = (String) json.get("state");
430 String lastHeard = (String) json.get("lastHeardFrom");
431 Recording recording = null;
432 if (StringUtils.isNotBlank(status) && StringUtils.isNotBlank(lastHeard)) {
433 recording = new RecordingImpl(recordingId, status, DateTimeSupport.fromUTC(lastHeard));
434 }
435 final Optional<Recording> recordingOpt = Optional.ofNullable(recording);
436 logger.info("Successfully get the technical metadata of event '{}' from the remote scheduler service",
437 eventId);
438 return new TechnicalMetadataImpl(recordingId, location, start, end, presenters, wfProperties,
439 agentConfig, recordingOpt);
440 }
441 }
442 } catch (NotFoundException | UnauthorizedException e) {
443 throw e;
444 } catch (Exception e) {
445 throw new SchedulerException("Unable to parse the technical metadata from remote scheduler service", e);
446 } finally {
447 closeConnection(response);
448 }
449 throw new SchedulerException("Unable to get the technical metadata from remote scheduler service");
450 }
451
452 @Override
453 public Map<String, String> getWorkflowConfig(String eventId)
454 throws NotFoundException, UnauthorizedException, SchedulerException {
455 HttpGet get = new HttpGet(eventId.concat("/workflow.properties"));
456 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
457 try {
458 if (response != null) {
459 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
460 throw new NotFoundException(
461 "Event workflow configuration '" + eventId + "' not found on remote scheduler service!");
462 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
463 logger.info("Unauthorized to get workflow config of the event {}.", eventId);
464 throw new UnauthorizedException("Unauthorized to get workflow config of the event " + eventId);
465 } else {
466 Properties properties = new Properties();
467 properties.load(response.getEntity().getContent());
468 logger.info("Successfully get event workflow configuration {} from the remote scheduler service", eventId);
469 return new HashMap<String, String>((Map) properties);
470 }
471 }
472 } catch (NotFoundException | UnauthorizedException e) {
473 throw e;
474 } catch (Exception e) {
475 throw new SchedulerException("Unable to parse event workflow configuration from remote scheduler service", e);
476 } finally {
477 closeConnection(response);
478 }
479 throw new SchedulerException("Unable to get event workflow configuration from remote scheduler service");
480 }
481
482 @Override
483 public Map<String, String> getCaptureAgentConfiguration(String eventId)
484 throws NotFoundException, UnauthorizedException, SchedulerException {
485 HttpGet get = new HttpGet(eventId.concat("/agent.properties"));
486 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
487 try {
488 if (response != null) {
489 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
490 throw new NotFoundException(
491 "Event capture agent configuration '" + eventId + "' not found on remote scheduler service!");
492 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
493 logger.info("Unauthorized to get capture agent config of the event {}.", eventId);
494 throw new UnauthorizedException("Unauthorized to get capture agent config of the event " + eventId);
495 } else {
496 Properties properties = new Properties();
497 properties.load(response.getEntity().getContent());
498 logger.info("Successfully get event capture agent configuration {} from the remote scheduler service",
499 eventId);
500 return new HashMap<String, String>((Map) properties);
501 }
502 }
503 } catch (NotFoundException | UnauthorizedException e) {
504 throw e;
505 } catch (Exception e) {
506 throw new SchedulerException(
507 "Unable to parse event capture agent configuration from remote scheduler service", e);
508 } finally {
509 closeConnection(response);
510 }
511 throw new SchedulerException("Unable to get event capture agent configuration from remote scheduler service");
512 }
513
514 @Override
515 public int getEventCount() throws SchedulerException, UnauthorizedException {
516 final HttpGet get = new HttpGet(UrlSupport.concat("eventCount"));
517 final HttpResponse response = getResponse(get, SC_OK, SC_UNAUTHORIZED);
518 try {
519 if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
520 logger.info("Unauthorized to get event count");
521 throw new UnauthorizedException("Unauthorized to get event count");
522 }
523 final String countString = EntityUtils.toString(response.getEntity(), UTF_8);
524 return Integer.parseInt(countString);
525 } catch (UnauthorizedException e) {
526 throw e;
527 } catch (Exception e) {
528 throw new SchedulerException("Unable to get event count from remote scheduler service", e);
529 } finally {
530 closeConnection(response);
531 }
532 }
533
534 @Override
535 public String getScheduleLastModified(String agentId) throws SchedulerException {
536 HttpGet get = new HttpGet(UrlSupport.concat(agentId, "lastmodified"));
537 HttpResponse response = getResponse(get, SC_OK);
538 try {
539 if (response != null) {
540 if (SC_OK == response.getStatusLine().getStatusCode()) {
541 String agentHash = EntityUtils.toString(response.getEntity(), UTF_8);
542 logger.info("Successfully get agent last modified hash of agent with id {} from the remote scheduler service",
543 agentId);
544 return agentHash;
545 }
546 }
547 } catch (Exception e) {
548 throw new SchedulerException("Unable to get agent last modified hash from remote scheduler service", e);
549 } finally {
550 closeConnection(response);
551 }
552 throw new SchedulerException("Unable to get agent last modified hash from remote scheduler service");
553 }
554
555 @Override
556 public List<MediaPackage> search(Optional<String> captureAgentId, Optional<Date> startsFrom, Optional<Date> startsTo,
557 Optional<Date> endFrom, Optional<Date> endTo) throws UnauthorizedException, SchedulerException {
558 List<NameValuePair> queryStringParams = new ArrayList<NameValuePair>();
559 if (captureAgentId.isPresent()) {
560 queryStringParams.add(new BasicNameValuePair("agent", captureAgentId.get()));
561 }
562 if (startsFrom.isPresent()) {
563 queryStringParams.add(new BasicNameValuePair("startsfrom", Long.toString(startsFrom.get().getTime())));
564 }
565 if (startsTo.isPresent()) {
566 queryStringParams.add(new BasicNameValuePair("startsto", Long.toString(startsTo.get().getTime())));
567 }
568 if (endFrom.isPresent()) {
569 queryStringParams.add(new BasicNameValuePair("endsfrom", Long.toString(endFrom.get().getTime())));
570 }
571 if (endTo.isPresent()) {
572 queryStringParams.add(new BasicNameValuePair("endsto", Long.toString(endTo.get().getTime())));
573 }
574 HttpGet get = new HttpGet("recordings.xml?".concat(URLEncodedUtils.format(queryStringParams, UTF_8)));
575 HttpResponse response = getResponse(get, SC_OK, SC_UNAUTHORIZED);
576 try {
577 if (response != null) {
578 if (SC_OK == response.getStatusLine().getStatusCode()) {
579 String mediaPackageXml = EntityUtils.toString(response.getEntity(), UTF_8);
580 List<MediaPackage> events = MediaPackageParser.getArrayFromXml(mediaPackageXml);
581 logger.info("Successfully get recordings from the remote scheduler service");
582 return events;
583 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
584 logger.info("Unauthorized to search for events");
585 throw new UnauthorizedException("Unauthorized to search for events");
586 }
587 }
588 } catch (UnauthorizedException e) {
589 throw e;
590 } catch (Exception e) {
591 throw new SchedulerException("Unable to get recordings from remote scheduler service", e);
592 } finally {
593 closeConnection(response);
594 }
595 throw new SchedulerException("Unable to get recordings from remote scheduler service");
596 }
597
598 @Override
599 public Optional<MediaPackage> getCurrentRecording(String captureAgentId)
600 throws SchedulerException, UnauthorizedException {
601 HttpGet get = new HttpGet(UrlSupport.concat("currentRecording", captureAgentId));
602 HttpResponse response = getResponse(get, SC_OK, SC_NO_CONTENT, SC_UNAUTHORIZED);
603 try {
604 if (SC_OK == response.getStatusLine().getStatusCode()) {
605 String mediaPackageXml = EntityUtils.toString(response.getEntity(), UTF_8);
606 MediaPackage event = MediaPackageParser.getFromXml(mediaPackageXml);
607 logger.info("Successfully get current recording of agent {} from the remote scheduler service", captureAgentId);
608 return Optional.of(event);
609 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
610 logger.info("Unauthorized to get current recording of agent {}", captureAgentId);
611 throw new UnauthorizedException("Unauthorized to get current recording of agent " + captureAgentId);
612 } else {
613 return Optional.empty();
614 }
615 } catch (UnauthorizedException e) {
616 throw e;
617 } catch (Exception e) {
618 throw new SchedulerException("Unable to get current recording from remote scheduler service", e);
619 } finally {
620 closeConnection(response);
621 }
622 }
623
624 @Override
625 public Optional<MediaPackage> getUpcomingRecording(String captureAgentId)
626 throws SchedulerException, UnauthorizedException {
627 HttpGet get = new HttpGet(UrlSupport.concat("upcomingRecording", captureAgentId));
628 HttpResponse response = getResponse(get, SC_OK, SC_NO_CONTENT, SC_UNAUTHORIZED);
629 try {
630 if (SC_OK == response.getStatusLine().getStatusCode()) {
631 String mediaPackageXml = EntityUtils.toString(response.getEntity(), UTF_8);
632 MediaPackage event = MediaPackageParser.getFromXml(mediaPackageXml);
633 logger.info("Successfully get upcoming recording of agent {} from the remote scheduler service",
634 captureAgentId);
635 return Optional.of(event);
636 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
637 logger.info("Unauthorized to get upcoming recording of agent {}", captureAgentId);
638 throw new UnauthorizedException("Unauthorized to get upcoming recording of agent " + captureAgentId);
639 } else {
640 return Optional.empty();
641 }
642 } catch (UnauthorizedException e) {
643 throw e;
644 } catch (Exception e) {
645 throw new SchedulerException("Unable to get upcoming recording from remote scheduler service", e);
646 } finally {
647 closeConnection(response);
648 }
649 }
650
651 @Override
652 public List<MediaPackage> findConflictingEvents(String captureDeviceID, Date startDate, Date endDate)
653 throws UnauthorizedException, SchedulerException {
654 List<NameValuePair> queryStringParams = new ArrayList<NameValuePair>();
655 queryStringParams.add(new BasicNameValuePair("agent", captureDeviceID));
656 queryStringParams.add(new BasicNameValuePair("start", Long.toString(startDate.getTime())));
657 queryStringParams.add(new BasicNameValuePair("end", Long.toString(endDate.getTime())));
658 HttpGet get = new HttpGet("conflicts.xml?".concat(URLEncodedUtils.format(queryStringParams, UTF_8)));
659 HttpResponse response = getResponse(get, SC_OK, SC_NO_CONTENT);
660 try {
661 if (response != null) {
662 if (SC_OK == response.getStatusLine().getStatusCode()) {
663 String mediaPackageXml = EntityUtils.toString(response.getEntity(), UTF_8);
664 List<MediaPackage> events = MediaPackageParser.getArrayFromXml(mediaPackageXml);
665 logger.info("Successfully get conflicts from the remote scheduler service");
666 return events;
667 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
668 logger.info("Unauthorized to search for conflicting events");
669 throw new UnauthorizedException("Unauthorized to search for conflicting events");
670 } else if (SC_NO_CONTENT == response.getStatusLine().getStatusCode()) {
671 return Collections.<MediaPackage> emptyList();
672 }
673 }
674 } catch (UnauthorizedException e) {
675 throw e;
676 } catch (Exception e) {
677 throw new SchedulerException("Unable to get conflicts from remote scheduler service", e);
678 } finally {
679 closeConnection(response);
680 }
681 throw new SchedulerException("Unable to get conflicts from remote scheduler service");
682 }
683
684 @Override
685 public List<MediaPackage> findConflictingEvents(String captureAgentId, RRule rrule, Date startDate, Date endDate,
686 long duration, TimeZone timezone) throws UnauthorizedException, SchedulerException {
687 List<NameValuePair> queryStringParams = new ArrayList<NameValuePair>();
688 queryStringParams.add(new BasicNameValuePair("agent", captureAgentId));
689 queryStringParams.add(new BasicNameValuePair("rrule", rrule.getRecur().toString()));
690 queryStringParams.add(new BasicNameValuePair("start", Long.toString(startDate.getTime())));
691 queryStringParams.add(new BasicNameValuePair("end", Long.toString(endDate.getTime())));
692 queryStringParams.add(new BasicNameValuePair("duration", Long.toString(duration)));
693 queryStringParams.add(new BasicNameValuePair("timezone", timezone.getID()));
694 HttpGet get = new HttpGet("conflicts.xml?".concat(URLEncodedUtils.format(queryStringParams, UTF_8)));
695 HttpResponse response = getResponse(get, SC_OK, SC_NO_CONTENT);
696 try {
697 if (response != null) {
698 if (SC_OK == response.getStatusLine().getStatusCode()) {
699 String mediaPackageXml = EntityUtils.toString(response.getEntity(), UTF_8);
700 List<MediaPackage> events = MediaPackageParser.getArrayFromXml(mediaPackageXml);
701 logger.info("Successfully get conflicts from the remote scheduler service");
702 return events;
703 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
704 logger.info("Unauthorized to search for conflicting events");
705 throw new UnauthorizedException("Unauthorized to search for conflicting events");
706 } else if (SC_NO_CONTENT == response.getStatusLine().getStatusCode()) {
707 return Collections.<MediaPackage> emptyList();
708 }
709 }
710 } catch (UnauthorizedException e) {
711 throw e;
712 } catch (Exception e) {
713 throw new SchedulerException("Unable to get conflicts from remote scheduler service", e);
714 } finally {
715 closeConnection(response);
716 }
717 throw new SchedulerException("Unable to get conflicts from remote scheduler service");
718 }
719
720 @Override
721 public String getCalendar(Optional<String> captureAgentId, Optional<String> seriesId, Optional<Date> cutoff)
722 throws SchedulerException {
723 List<NameValuePair> queryStringParams = new ArrayList<NameValuePair>();
724 if (captureAgentId.isPresent()) {
725 queryStringParams.add(new BasicNameValuePair("agentid", captureAgentId.get()));
726 }
727 if (seriesId.isPresent()) {
728 queryStringParams.add(new BasicNameValuePair("seriesid", seriesId.get()));
729 }
730 if (cutoff.isPresent()) {
731 queryStringParams.add(new BasicNameValuePair("cutoff", Long.toString(cutoff.get().getTime())));
732 }
733 HttpGet get = new HttpGet("calendars?".concat(URLEncodedUtils.format(queryStringParams, UTF_8)));
734 HttpResponse response = getResponse(get, SC_OK);
735 try {
736 if (response != null) {
737 if (SC_OK == response.getStatusLine().getStatusCode()) {
738 String calendar = EntityUtils.toString(response.getEntity(), UTF_8);
739 logger.info("Successfully get calendar of agent with id {} from the remote scheduler service",
740 captureAgentId);
741 return calendar;
742 }
743 }
744 } catch (Exception e) {
745 throw new SchedulerException("Unable to get calendar from remote scheduler service", e);
746 } finally {
747 closeConnection(response);
748 }
749 throw new SchedulerException("Unable to get calendar from remote scheduler service");
750 }
751
752 @Override
753 public void removeScheduledRecordingsBeforeBuffer(long buffer) throws UnauthorizedException, SchedulerException {
754 HttpPost post = new HttpPost("/removeOldScheduledRecordings");
755 logger.debug("Start removing old schedules before buffer {} through remote Schedule Service", buffer);
756
757 List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
758 params.add(new BasicNameValuePair("buffer", Long.toString(buffer)));
759 post.setEntity(new UrlEncodedFormEntity(params, UTF_8));
760
761 HttpResponse response = getResponse(post, SC_OK, SC_UNAUTHORIZED);
762 try {
763 if (response != null && SC_OK == response.getStatusLine().getStatusCode()) {
764 logger.info("Successfully started removing old schedules before butter {} to the scheduler service", buffer);
765 return;
766 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
767 logger.info("Unauthorized to remove old schedules before buffer {}.", buffer);
768 throw new UnauthorizedException("Unauthorized to remove old schedules");
769 }
770 } catch (UnauthorizedException e) {
771 throw e;
772 } catch (Exception e) {
773 throw new SchedulerException("Unable to remove old schedules from the scheduler service", e);
774 } finally {
775 closeConnection(response);
776 }
777 throw new SchedulerException("Unable to remove old schedules from the scheduler service");
778 }
779
780 @Override
781 public boolean updateRecordingState(String mediapackageId, String state)
782 throws NotFoundException, SchedulerException {
783 HttpPut put = new HttpPut(UrlSupport.concat(mediapackageId, "recordingStatus"));
784
785 List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
786 params.add(new BasicNameValuePair("state", state));
787 put.setEntity(new UrlEncodedFormEntity(params, UTF_8));
788
789 HttpResponse response = getResponse(put, SC_OK, SC_NOT_FOUND, SC_BAD_REQUEST);
790 try {
791 if (response != null) {
792 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
793 logger.warn("Event with mediapackage id {} was not found by the scheduler service", mediapackageId);
794 throw new NotFoundException(
795 "Event with mediapackage id '" + mediapackageId + "' not found on remote scheduler service!");
796 } else if (SC_BAD_REQUEST == response.getStatusLine().getStatusCode()) {
797 logger.info("Unable to update event with mediapackage id {}, invalid recording state: {}.", mediapackageId,
798 state);
799 return false;
800 } else if (SC_OK == response.getStatusLine().getStatusCode()) {
801 logger.info("Event with mediapackage id {} successfully updated with recording status.", mediapackageId);
802 return true;
803 } else {
804 throw new SchedulerException("Unexpected status code " + response.getStatusLine());
805 }
806 }
807 } catch (NotFoundException e) {
808 throw e;
809 } catch (Exception e) {
810 throw new SchedulerException("Unable to update recording state of event with mediapackage id " + mediapackageId
811 + " to the scheduler service", e);
812 } finally {
813 closeConnection(response);
814 }
815 throw new SchedulerException("Unable to update recording state of event with mediapackage id " + mediapackageId);
816 }
817
818 @Override
819 public Recording getRecordingState(String id) throws NotFoundException, SchedulerException {
820 HttpGet get = new HttpGet(UrlSupport.concat(id, "recordingStatus"));
821 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND);
822 try {
823 if (response != null) {
824 if (SC_OK == response.getStatusLine().getStatusCode()) {
825 String recordingStateJson = EntityUtils.toString(response.getEntity(), UTF_8);
826 JSONObject json = (JSONObject) parser.parse(recordingStateJson);
827 String recordingId = (String) json.get("id");
828 String status = (String) json.get("state");
829 Long lastHeard = (Long) json.get("lastHeardFrom");
830 logger.info("Successfully get calendar of agent with id {} from the remote scheduler service", id);
831 return new RecordingImpl(recordingId, status, lastHeard);
832 } else if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
833 logger.warn("Event with mediapackage id {} was not found by the scheduler service", id);
834 throw new NotFoundException("Event with mediapackage id '" + id + "' not found on remote scheduler service!");
835 }
836 }
837 } catch (NotFoundException e) {
838 throw e;
839 } catch (Exception e) {
840 throw new SchedulerException("Unable to get calendar from remote scheduler service", e);
841 } finally {
842 closeConnection(response);
843 }
844 throw new SchedulerException("Unable to get calendar from remote scheduler service");
845 }
846
847 @Override
848 public void removeRecording(String eventId) throws NotFoundException, SchedulerException {
849 HttpDelete delete = new HttpDelete(UrlSupport.concat(eventId, "recordingStatus"));
850
851 HttpResponse response = getResponse(delete, SC_OK, SC_NOT_FOUND);
852 try {
853 if (response != null && SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
854 logger.info("Event {} was not found by the scheduler service", eventId);
855 throw new NotFoundException("Event '" + eventId + "' not found on remote scheduler service!");
856 } else if (response != null && SC_OK == response.getStatusLine().getStatusCode()) {
857 logger.info("Recording status of event {} removed from scheduling service.", eventId);
858 return;
859 }
860 } catch (NotFoundException e) {
861 throw e;
862 } catch (Exception e) {
863 throw new SchedulerException(
864 "Unable to remove recording status of event " + eventId + " from the scheduler service", e);
865 } finally {
866 closeConnection(response);
867 }
868 throw new SchedulerException("Unable to remove recording status of event " + eventId);
869 }
870
871 @Override
872 public Map<String, Recording> getKnownRecordings() throws SchedulerException {
873 HttpGet get = new HttpGet("recordingStatus");
874 HttpResponse response = getResponse(get, SC_OK);
875 try {
876 if (response != null) {
877 if (SC_OK == response.getStatusLine().getStatusCode()) {
878 String recordingStates = EntityUtils.toString(response.getEntity(), UTF_8);
879 JSONArray recordings = (JSONArray) parser.parse(recordingStates);
880 Map<String, Recording> recordingsMap = new HashMap<String, Recording>();
881 for (int i = 0; i < recordings.size(); i++) {
882 JSONObject recording = (JSONObject) recordings.get(i);
883 String recordingId = (String) recording.get("id");
884 String status = (String) recording.get("state");
885 Long lastHeard = (Long) recording.get("lastHeardFrom");
886 recordingsMap.put(recordingId, new RecordingImpl(recordingId, status, lastHeard));
887 }
888 logger.info("Successfully get recording states from the remote scheduler service");
889 return recordingsMap;
890 }
891 }
892 } catch (Exception e) {
893 throw new SchedulerException("Unable to get recording states from remote scheduler service", e);
894 } finally {
895 closeConnection(response);
896 }
897 throw new SchedulerException("Unable to get recording states from remote scheduler service");
898 }
899
900 private String toPropertyString(Map<String, String> properties) {
901 StringBuilder wfPropertiesString = new StringBuilder();
902 for (Map.Entry<String, String> entry : properties.entrySet()) {
903 wfPropertiesString.append(entry.getKey() + "=" + entry.getValue() + "\n");
904 }
905 return wfPropertiesString.toString();
906 }
907
908 @Reference
909 @Override
910 public void setTrustedHttpClient(TrustedHttpClient trustedHttpClient) {
911 super.setTrustedHttpClient(trustedHttpClient);
912 }
913 @Reference
914 @Override
915 public void setRemoteServiceManager(ServiceRegistry serviceRegistry) {
916 super.setRemoteServiceManager(serviceRegistry);
917 }
918
919 }