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