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.series.remote;
23
24 import static java.lang.String.format;
25 import static javax.servlet.http.HttpServletResponse.SC_OK;
26 import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
27 import static javax.ws.rs.core.Response.Status.NOT_FOUND;
28 import static org.apache.commons.lang3.StringUtils.isBlank;
29 import static org.apache.http.HttpStatus.SC_BAD_REQUEST;
30 import static org.apache.http.HttpStatus.SC_CREATED;
31 import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
32 import static org.apache.http.HttpStatus.SC_NOT_FOUND;
33 import static org.apache.http.HttpStatus.SC_NO_CONTENT;
34 import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
35
36 import org.opencastproject.metadata.dublincore.DublinCore;
37 import org.opencastproject.metadata.dublincore.DublinCoreCatalog;
38 import org.opencastproject.metadata.dublincore.DublinCores;
39 import org.opencastproject.security.api.AccessControlList;
40 import org.opencastproject.security.api.AccessControlParser;
41 import org.opencastproject.security.api.TrustedHttpClient;
42 import org.opencastproject.security.api.UnauthorizedException;
43 import org.opencastproject.series.api.Series;
44 import org.opencastproject.series.api.SeriesException;
45 import org.opencastproject.series.api.SeriesService;
46 import org.opencastproject.serviceregistry.api.RemoteBase;
47 import org.opencastproject.serviceregistry.api.ServiceRegistry;
48 import org.opencastproject.util.NotFoundException;
49 import org.opencastproject.util.doc.rest.RestService;
50
51 import com.google.gson.Gson;
52 import com.google.gson.reflect.TypeToken;
53
54 import org.apache.commons.io.IOUtils;
55 import org.apache.http.HttpResponse;
56 import org.apache.http.NameValuePair;
57 import org.apache.http.client.entity.UrlEncodedFormEntity;
58 import org.apache.http.client.methods.HttpDelete;
59 import org.apache.http.client.methods.HttpGet;
60 import org.apache.http.client.methods.HttpPost;
61 import org.apache.http.client.methods.HttpPut;
62 import org.apache.http.client.utils.URLEncodedUtils;
63 import org.apache.http.entity.ByteArrayEntity;
64 import org.apache.http.entity.ContentType;
65 import org.apache.http.message.BasicNameValuePair;
66 import org.codehaus.jettison.json.JSONArray;
67 import org.codehaus.jettison.json.JSONObject;
68 import org.json.simple.parser.JSONParser;
69 import org.osgi.service.component.annotations.Component;
70 import org.osgi.service.component.annotations.Reference;
71 import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75 import java.io.IOException;
76 import java.io.InputStreamReader;
77 import java.io.Reader;
78 import java.io.StringWriter;
79 import java.lang.reflect.Type;
80 import java.nio.charset.StandardCharsets;
81 import java.util.ArrayList;
82 import java.util.Date;
83 import java.util.HashMap;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.Optional;
87 import java.util.TreeMap;
88
89 import javax.ws.rs.GET;
90 import javax.ws.rs.Path;
91 import javax.ws.rs.PathParam;
92 import javax.ws.rs.Produces;
93 import javax.ws.rs.WebApplicationException;
94 import javax.ws.rs.core.MediaType;
95 import javax.ws.rs.core.Response;
96
97
98
99
100 @Path("/series")
101 @RestService(
102 name = "seriesservice",
103 title = "Series Service Remote",
104 abstractText = "This service creates, edits and retrieves and helps managing series.",
105 notes = {
106 "All paths above are relative to the REST endpoint base (something like http://your.server/files)",
107 "If the service is down or not working it will return a status 503, this means the the "
108 + "underlying service is not working and is either restarting or has failed",
109 "A status code 500 means a general failure has occurred which is not recoverable and was "
110 + "not anticipated. In other words, there is a bug! You should file an error report "
111 + "with your server logs from the time when the error occurred: "
112 + "<a href=\"https://github.com/opencast/opencast/issues\">Opencast Issue Tracker</a>"
113 }
114 )
115 @Component(
116 property = {
117 "service.description=Series Remote Service Proxy",
118 "opencast.service.type=org.opencastproject.series",
119 "opencast.service.path=/series",
120 "opencast.service.publish=false"
121 },
122 immediate = true,
123 service = { SeriesService.class, SeriesServiceRemoteImpl.class }
124 )
125 @JaxrsResource
126 public class SeriesServiceRemoteImpl extends RemoteBase implements SeriesService {
127
128 private static final Logger logger = LoggerFactory.getLogger(SeriesServiceRemoteImpl.class);
129
130
131 private static final Gson gson = new Gson();
132 private static final Type seriesListType = new TypeToken<ArrayList<Series>>() { }.getType();
133
134 public SeriesServiceRemoteImpl() {
135 super(JOB_TYPE);
136 }
137
138
139
140
141
142
143 @Override
144 @Reference
145 public void setTrustedHttpClient(TrustedHttpClient client) {
146 super.setTrustedHttpClient(client);
147 }
148
149
150
151
152
153
154 @Override
155 @Reference
156 public void setRemoteServiceManager(ServiceRegistry remoteServiceManager) {
157 super.setRemoteServiceManager(remoteServiceManager);
158 }
159
160 @Override
161 public DublinCoreCatalog updateSeries(DublinCoreCatalog dc) throws SeriesException, UnauthorizedException {
162 String seriesId = dc.getFirst(DublinCore.PROPERTY_IDENTIFIER);
163
164 HttpPost post = new HttpPost("/");
165 try {
166 List<BasicNameValuePair> params = new ArrayList<>();
167 params.add(new BasicNameValuePair("series", dc.toXmlString()));
168 post.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
169 } catch (Exception e) {
170 throw new SeriesException("Unable to assemble a remote series request for updating series " + seriesId, e);
171 }
172
173 HttpResponse response = getResponse(post, SC_NO_CONTENT, SC_CREATED, SC_UNAUTHORIZED);
174 try {
175 if (response != null) {
176 int statusCode = response.getStatusLine().getStatusCode();
177 if (SC_NO_CONTENT == statusCode) {
178 logger.info("Successfully updated series {} in the series service", seriesId);
179 return null;
180 } else if (SC_UNAUTHORIZED == statusCode) {
181 throw new UnauthorizedException("Not authorized to update series " + seriesId);
182 } else if (SC_CREATED == statusCode) {
183 DublinCoreCatalog catalogImpl = DublinCores.read(response.getEntity().getContent());
184 logger.info("Successfully created series {} in the series service", seriesId);
185 return catalogImpl;
186 }
187 }
188 } catch (UnauthorizedException e) {
189 throw e;
190 } catch (Exception e) {
191 throw new SeriesException("Unable to update series " + seriesId + " using the remote series services: " + e);
192 } finally {
193 closeConnection(response);
194 }
195 throw new SeriesException("Unable to update series " + seriesId + " using the remote series services");
196 }
197
198 @Override
199 public boolean updateAccessControl(String seriesID, AccessControlList accessControl)
200 throws NotFoundException, SeriesException, UnauthorizedException {
201 return updateAccessControl(seriesID, accessControl, false);
202 }
203
204 @Override
205 public boolean updateAccessControl(String seriesID, AccessControlList accessControl, boolean overrideEpisodeAcl)
206 throws NotFoundException, SeriesException, UnauthorizedException {
207 HttpPost post = new HttpPost(seriesID + "/accesscontrol");
208 try {
209 List<BasicNameValuePair> params = new ArrayList<>();
210 params.add(new BasicNameValuePair("seriesID", seriesID));
211 params.add(new BasicNameValuePair("acl", AccessControlParser.toXml(accessControl)));
212 params.add(new BasicNameValuePair("overrideEpisodeAcl", Boolean.toString(overrideEpisodeAcl)));
213 post.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
214 } catch (Exception e) {
215 throw new SeriesException("Unable to assemble a remote series request for updating an ACL " + accessControl, e);
216 }
217
218 HttpResponse response = getResponse(post, SC_NO_CONTENT, SC_CREATED, SC_NOT_FOUND, SC_UNAUTHORIZED);
219 try {
220 if (response != null) {
221 int status = response.getStatusLine().getStatusCode();
222 if (SC_NOT_FOUND == status) {
223 throw new NotFoundException("Series not found: " + seriesID);
224 } else if (SC_NO_CONTENT == status) {
225 logger.info("Successfully updated ACL of {} to the series service", seriesID);
226 return true;
227 } else if (SC_UNAUTHORIZED == status) {
228 throw new UnauthorizedException("Not authorized to update series ACL of " + seriesID);
229 } else if (SC_CREATED == status) {
230 logger.info("Successfully created ACL of {} to the series service", seriesID);
231 return false;
232 }
233 }
234 } finally {
235 closeConnection(response);
236 }
237 throw new SeriesException("Unable to update series ACL " + accessControl + " using the remote series services");
238 }
239
240 @Override
241 public void deleteSeries(String seriesID) throws SeriesException, NotFoundException, UnauthorizedException {
242 HttpDelete del = new HttpDelete(seriesID);
243 HttpResponse response = getResponse(del, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
244 try {
245 if (response != null) {
246 int statusCode = response.getStatusLine().getStatusCode();
247 if (SC_NOT_FOUND == statusCode) {
248 throw new NotFoundException("Series not found: " + seriesID);
249 } else if (SC_UNAUTHORIZED == statusCode) {
250 throw new UnauthorizedException("Not authorized to delete series " + seriesID);
251 } else if (SC_OK == statusCode) {
252 logger.info("Successfully deleted {} from the remote series index", seriesID);
253 return;
254 }
255 }
256 } finally {
257 closeConnection(response);
258 }
259 throw new SeriesException("Unable to remove " + seriesID + " from a remote series index");
260 }
261
262 @GET
263 @Produces(MediaType.APPLICATION_JSON)
264 @Path("{seriesID:.+}.json")
265 public Response getSeriesJSON(@PathParam("seriesID") String seriesID) throws UnauthorizedException {
266 logger.debug("Series Lookup: {}", seriesID);
267 try {
268 DublinCoreCatalog dc = getSeries(seriesID);
269 return Response.ok(dc.toJson()).build();
270 } catch (NotFoundException e) {
271 return Response.status(NOT_FOUND).build();
272 } catch (UnauthorizedException e) {
273 throw e;
274 } catch (Exception e) {
275 logger.error("Could not retrieve series: {}", e.getMessage());
276 throw new WebApplicationException(INTERNAL_SERVER_ERROR);
277 }
278 }
279
280 @GET
281 @Produces(MediaType.APPLICATION_JSON)
282 @Path("/{seriesID:.+}/acl.json")
283 public Response getSeriesAccessControlListJson(@PathParam("seriesID") String seriesID) {
284 logger.debug("Series ACL lookup: {}", seriesID);
285 try {
286 AccessControlList acl = getSeriesAccessControl(seriesID);
287 return Response.ok(acl).build();
288 } catch (NotFoundException e) {
289 return Response.status(NOT_FOUND).build();
290 } catch (SeriesException e) {
291 logger.error("Could not retrieve series ACL: {}", e.getMessage());
292 throw new WebApplicationException(INTERNAL_SERVER_ERROR);
293 }
294 }
295
296 @Override
297 public DublinCoreCatalog getSeries(String seriesID) throws SeriesException, NotFoundException, UnauthorizedException {
298 HttpGet get = new HttpGet(seriesID + ".xml");
299 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
300 try {
301 if (response != null) {
302 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
303 throw new NotFoundException("Series " + seriesID + " not found in remote series index!");
304 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
305 throw new UnauthorizedException("Not authorized to get series " + seriesID);
306 } else {
307 DublinCoreCatalog dublinCoreCatalog = DublinCores.read(response.getEntity().getContent());
308 logger.debug("Successfully received series {} from the remote series index", seriesID);
309 return dublinCoreCatalog;
310 }
311 }
312 } catch (UnauthorizedException e) {
313 throw e;
314 } catch (NotFoundException e) {
315 throw e;
316 } catch (Exception e) {
317 throw new SeriesException("Unable to parse series from remote series index: " + e);
318 } finally {
319 closeConnection(response);
320 }
321 throw new SeriesException("Unable to get series from remote series index");
322 }
323
324 @Override
325 public List<Series> getAllForAdministrativeRead(Date from, Optional<Date> to, int limit)
326 throws SeriesException, UnauthorizedException {
327
328 StringBuilder url = new StringBuilder();
329 url.append("/allInRangeAdministrative.json?");
330
331 List<NameValuePair> queryParams = new ArrayList<>();
332 queryParams.add(new BasicNameValuePair("from", Long.toString(from.getTime())));
333 queryParams.add(new BasicNameValuePair("limit", Integer.toString(limit)));
334 if (to.isPresent()) {
335 queryParams.add(new BasicNameValuePair("to", Long.toString(to.get().getTime())));
336 }
337 url.append(URLEncodedUtils.format(queryParams, StandardCharsets.UTF_8));
338 HttpGet get = new HttpGet(url.toString());
339
340
341 HttpResponse response = getResponse(get, SC_OK, SC_BAD_REQUEST, SC_UNAUTHORIZED);
342 try {
343 if (response == null) {
344 throw new SeriesException("Unable to get series from remote series index");
345 }
346
347 if (response.getStatusLine().getStatusCode() == SC_BAD_REQUEST) {
348 throw new SeriesException("internal server error when fetching /allInRangeAdministrative.json");
349 } else if (response.getStatusLine().getStatusCode() == SC_UNAUTHORIZED) {
350 throw new UnauthorizedException("got UNAUTHORIZED when fetching /allInRangeAdministrative.json");
351 } else {
352
353 Reader reader = new InputStreamReader(response.getEntity().getContent(), "UTF-8");
354 return gson.fromJson(reader, seriesListType);
355 }
356 } catch (IOException e) {
357 throw new SeriesException("failed to reader response body of /allInRangeAdministrative.json", e);
358 } finally {
359 closeConnection(response);
360 }
361 }
362
363 @Override
364 public AccessControlList getSeriesAccessControl(String seriesID) throws NotFoundException, SeriesException {
365 HttpGet get = new HttpGet(seriesID + "/acl.xml");
366 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND);
367 try {
368 if (response != null) {
369 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
370 throw new NotFoundException("Series ACL " + seriesID + " not found on remote series index!");
371 } else {
372 AccessControlList acl = AccessControlParser.parseAcl(response.getEntity().getContent());
373 logger.info("Successfully get series ACL {} from the remote series index", seriesID);
374 return acl;
375 }
376 }
377 } catch (NotFoundException e) {
378 throw e;
379 } catch (Exception e) {
380 throw new SeriesException("Unable to parse series ACL form remote series index: " + e);
381 } finally {
382 closeConnection(response);
383 }
384 throw new SeriesException("Unable to get series ACL from remote series index");
385 }
386
387 @Override
388 public int getSeriesCount() throws SeriesException {
389 HttpGet get = new HttpGet("/count");
390 HttpResponse response = getResponse(get);
391 try {
392 if (response != null) {
393 int count = Integer.parseInt(IOUtils.toString(response.getEntity().getContent()));
394 logger.info("Successfully get series dublin core catalog list from the remote series index");
395 return count;
396 }
397 } catch (Exception e) {
398 throw new SeriesException("Unable to count series from remote series index: " + e);
399 } finally {
400 closeConnection(response);
401 }
402 throw new SeriesException("Unable to count series from remote series index");
403 }
404
405 @Override
406 public Map<String, String> getSeriesProperties(String seriesID)
407 throws SeriesException, NotFoundException, UnauthorizedException {
408 HttpGet get = new HttpGet(seriesID + "/properties.json");
409 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
410 JSONParser parser = new JSONParser();
411 try {
412 if (response != null) {
413 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
414 throw new NotFoundException("Series " + seriesID + " not found in remote series index!");
415 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
416 throw new UnauthorizedException("Not authorized to get series " + seriesID);
417 } else {
418 logger.debug("Successfully received series {} properties from the remote series index", seriesID);
419 StringWriter writer = new StringWriter();
420 IOUtils.copy(response.getEntity().getContent(), writer, StandardCharsets.UTF_8);
421 JSONArray jsonProperties = (JSONArray) parser.parse(writer.toString());
422 Map<String, String> properties = new TreeMap<>();
423 for (int i = 0; i < jsonProperties.length(); i++) {
424 JSONObject property = (JSONObject) jsonProperties.get(i);
425 JSONArray names = property.names();
426 for (int j = 0; j < names.length(); j++) {
427 properties.put(names.get(j).toString(), property.get(names.get(j).toString()).toString());
428 }
429 }
430 return properties;
431 }
432 }
433 } catch (UnauthorizedException e) {
434 throw e;
435 } catch (NotFoundException e) {
436 throw e;
437 } catch (Exception e) {
438 throw new SeriesException("Unable to parse series properties from remote series index: " + e);
439 } finally {
440 closeConnection(response);
441 }
442 throw new SeriesException("Unable to get series from remote series index");
443 }
444
445 @Override
446 public String getSeriesProperty(String seriesID, String propertyName)
447 throws SeriesException, NotFoundException, UnauthorizedException {
448 HttpGet get = new HttpGet(seriesID + "/property/" + propertyName + ".json");
449 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
450 try {
451 if (response != null) {
452 if (SC_NOT_FOUND == response.getStatusLine().getStatusCode()) {
453 throw new NotFoundException("Series " + seriesID + " not found in remote series index!");
454 } else if (SC_UNAUTHORIZED == response.getStatusLine().getStatusCode()) {
455 throw new UnauthorizedException("Not authorized to get series " + seriesID);
456 } else {
457 logger.debug("Successfully received series {} property {} from the remote series index", seriesID,
458 propertyName);
459 StringWriter writer = new StringWriter();
460 IOUtils.copy(response.getEntity().getContent(), writer, StandardCharsets.UTF_8);
461 return writer.toString();
462 }
463 }
464 } catch (UnauthorizedException e) {
465 throw e;
466 } catch (NotFoundException e) {
467 throw e;
468 } catch (Exception e) {
469 throw new SeriesException("Unable to parse series from remote series index: " + e);
470 } finally {
471 closeConnection(response);
472 }
473 throw new SeriesException("Unable to get series from remote series index");
474 }
475
476 @Override
477 public void updateSeriesProperty(String seriesID, String propertyName, String propertyValue)
478 throws SeriesException, NotFoundException, UnauthorizedException {
479 HttpPost post = new HttpPost("/" + seriesID + "/property");
480 try {
481 List<BasicNameValuePair> params = new ArrayList<>();
482 params.add(new BasicNameValuePair("name", propertyName));
483 params.add(new BasicNameValuePair("value", propertyValue));
484 post.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
485 } catch (Exception e) {
486 throw new SeriesException("Unable to assemble a remote series request for updating series " + seriesID
487 + " series property " + propertyName + ":" + propertyValue, e);
488 }
489
490 HttpResponse response = getResponse(post, SC_NO_CONTENT, SC_CREATED, SC_UNAUTHORIZED);
491 try {
492 if (response != null) {
493 int statusCode = response.getStatusLine().getStatusCode();
494 if (SC_NO_CONTENT == statusCode) {
495 logger.info("Successfully updated series {} with property name {} and value {} in the series service",
496 seriesID, propertyName, propertyValue);
497 return;
498 } else if (SC_UNAUTHORIZED == statusCode) {
499 throw new UnauthorizedException("Not authorized to update series " + seriesID);
500 }
501 }
502 } catch (UnauthorizedException e) {
503 throw e;
504 } catch (Exception e) {
505 throw new SeriesException("Unable to update series " + seriesID + " with property " + propertyName + ":"
506 + propertyValue + " using the remote series services: ", e);
507 } finally {
508 closeConnection(response);
509 }
510 throw new SeriesException("Unable to update series " + seriesID + " using the remote series services");
511 }
512
513 @Override
514 public void deleteSeriesProperty(String seriesID, String propertyName)
515 throws SeriesException, NotFoundException, UnauthorizedException {
516 HttpDelete del = new HttpDelete("/" + seriesID + "/property/" + propertyName);
517 HttpResponse response = getResponse(del, SC_OK, SC_NOT_FOUND, SC_UNAUTHORIZED);
518 try {
519 if (response != null) {
520 int statusCode = response.getStatusLine().getStatusCode();
521 if (SC_NOT_FOUND == statusCode) {
522 throw new NotFoundException("Series not found: " + seriesID);
523 } else if (SC_UNAUTHORIZED == statusCode) {
524 throw new UnauthorizedException("Not authorized to delete series " + seriesID);
525 } else if (SC_OK == statusCode) {
526 logger.info("Successfully deleted {} from the remote series index", seriesID);
527 return;
528 }
529 }
530 } finally {
531 closeConnection(response);
532 }
533 throw new SeriesException("Unable to remove " + seriesID + " from a remote series index");
534 }
535
536 @Override
537 public boolean updateExtendedMetadata(String seriesId, String type, DublinCoreCatalog dc) throws SeriesException {
538 HttpPut put = new HttpPut("/" + seriesId + "/extendedMetadata/" + type);
539 try {
540 List<BasicNameValuePair> params = new ArrayList<>();
541 params.add(new BasicNameValuePair("dc", dc.toXmlString()));
542 put.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
543 } catch (Exception e) {
544 throw new SeriesException("Unable to assemble a remote series request for updating extended metadata of series "
545 + seriesId, e);
546 }
547
548 HttpResponse response = getResponse(put, SC_NO_CONTENT, SC_CREATED, SC_INTERNAL_SERVER_ERROR);
549 try {
550 if (response == null) {
551 throw new SeriesException(format("Error while updating extended metadata catalog of type '%s' for series '%s'",
552 type, seriesId));
553 } else {
554 final int statusCode = response.getStatusLine().getStatusCode();
555 switch (statusCode) {
556 case SC_NO_CONTENT:
557 case SC_CREATED:
558 return true;
559 case SC_INTERNAL_SERVER_ERROR:
560 throw new SeriesException(
561 format("Error while updating extended metadata catalog of type '%s' for series '%s'", type,
562 seriesId));
563 default:
564 throw new SeriesException(format("Unexpected status code", statusCode));
565 }
566 }
567 } finally {
568 closeConnection(response);
569 }
570 }
571
572 @Override
573 public Optional<Map<String, byte[]>> getSeriesElements(String seriesID) throws SeriesException {
574 HttpGet get = new HttpGet("/" + seriesID + "/elements.json");
575 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_INTERNAL_SERVER_ERROR);
576 JSONParser parser = new JSONParser();
577
578 try {
579 if (response == null) {
580 throw new SeriesException(format("Error while retrieving elements from series '%s'", seriesID));
581 } else {
582 final int statusCode = response.getStatusLine().getStatusCode();
583 switch (statusCode) {
584 case SC_OK:
585 JSONArray elementArray = (JSONArray) parser.parse(IOUtils.toString(response.getEntity().getContent()));
586 Map<String, byte[]> elements = new HashMap<>();
587 for (int i = 0; i < elementArray.length(); i++) {
588 final String type = elementArray.getString(i);
589 Optional<byte[]> optData = getSeriesElementData(seriesID, type);
590 if (optData.isPresent()) {
591 elements.put(type, optData.get());
592 } else {
593 throw new SeriesException(format("Tried to load non-existing element of type '%s'", type));
594 }
595 }
596 return Optional.of(elements);
597 case SC_NOT_FOUND:
598 return Optional.empty();
599 case SC_INTERNAL_SERVER_ERROR:
600 throw new SeriesException(format("Error while retrieving elements from series '%s'", seriesID));
601 default:
602 throw new SeriesException(format("Unexpected status code", statusCode));
603 }
604 }
605 } catch (Exception e) {
606 logger.warn("Error while retrieving elements from remote service:", e);
607 throw new SeriesException(e);
608 } finally {
609 closeConnection(response);
610 }
611 }
612
613 @Override
614 public Optional<byte[]> getSeriesElementData(String seriesID, String type) throws SeriesException {
615 HttpGet get = new HttpGet("/" + seriesID + "/elements/" + type);
616 HttpResponse response = getResponse(get, SC_OK, SC_NOT_FOUND, SC_INTERNAL_SERVER_ERROR);
617
618 try {
619 if (response == null) {
620 throw new SeriesException(
621 format("Error while retrieving element of type '%s' from series '%s'", type, seriesID));
622 } else {
623 final int statusCode = response.getStatusLine().getStatusCode();
624 switch (statusCode) {
625 case SC_OK:
626 return Optional.of(IOUtils.toByteArray(response.getEntity().getContent()));
627 case SC_NOT_FOUND:
628 return Optional.empty();
629 case SC_INTERNAL_SERVER_ERROR:
630 throw new SeriesException(
631 format("Error while retrieving element of type '%s' from series '%s'", type, seriesID));
632 default:
633 throw new SeriesException(format("Unexpected status code", statusCode));
634 }
635 }
636 } catch (Exception e) {
637 logger.warn("Error while retrieving element from remote service:", e);
638 throw new SeriesException(e);
639 } finally {
640 closeConnection(response);
641 }
642 }
643
644 @Override
645 public boolean updateSeriesElement(String seriesID, String type, byte[] data) throws SeriesException {
646 HttpPut put = new HttpPut("/" + seriesID + "/elements/" + type);
647 put.setEntity(new ByteArrayEntity(data, ContentType.DEFAULT_BINARY));
648
649 HttpResponse response = getResponse(put, SC_CREATED, SC_NO_CONTENT, SC_INTERNAL_SERVER_ERROR);
650 try {
651 if (response == null) {
652 throw new SeriesException(format("Error while updating element of type '%s' in series '%s'", type, seriesID));
653 } else {
654 final int statusCode = response.getStatusLine().getStatusCode();
655 switch (statusCode) {
656 case SC_NO_CONTENT:
657 case SC_CREATED:
658 return true;
659 case SC_INTERNAL_SERVER_ERROR:
660 throw new SeriesException(
661 format("Error while updating element of type '%s' in series '%s'", type, seriesID));
662 default:
663 throw new SeriesException(format("Unexpected status code", statusCode));
664 }
665 }
666 } finally {
667 closeConnection(response);
668 }
669 }
670
671 @Override
672 public boolean deleteSeriesElement(String seriesID, String type) throws SeriesException {
673 if (isBlank(seriesID)) {
674 throw new IllegalArgumentException("Series ID must not be blank");
675 }
676 if (isBlank(type)) {
677 throw new IllegalArgumentException("Element type must not be blank");
678 }
679
680 HttpDelete del = new HttpDelete("/" + seriesID + "/elements/" + type);
681 HttpResponse response = getResponse(del, SC_NO_CONTENT, SC_NOT_FOUND, SC_INTERNAL_SERVER_ERROR);
682 try {
683 if (response == null) {
684 throw new SeriesException("Unable to remove " + seriesID + " from a remote series index");
685 } else {
686 final int statusCode = response.getStatusLine().getStatusCode();
687 switch (statusCode) {
688 case SC_NO_CONTENT:
689 return true;
690 case SC_NOT_FOUND:
691 return false;
692 case SC_INTERNAL_SERVER_ERROR:
693 throw new SeriesException(
694 format("Error while deleting element of type '%s' from series '%s'", type, seriesID));
695 default:
696 throw new SeriesException(format("Unexpected status code", statusCode));
697 }
698 }
699 } finally {
700 closeConnection(response);
701 }
702 }
703 }